source: rtems/cpukit/libfs/src/dosfs/msdos_dir.c @ cfe8f7a

5
Last change on this file since cfe8f7a was cfe8f7a, checked in by Sebastian Huber <sebastian.huber@…>, on 04/27/20 at 14:14:06

doxygen: Switch @brief and @ingroup

This order change fixes the Latex documentation build via Doxygen.

  • Property mode set to 100644
File size: 14.3 KB
Line 
1/**
2 * @file
3 *
4 * @ingroup libfs_msdos MSDOS FileSystem
5 *
6 * @brief MSDOS Directory Handlers Implementation
7 */
8
9/*
10 *  Copyright (C) 2001 OKTET Ltd., St.-Petersburg, Russia
11 *  Author: Eugeny S. Mints <Eugeny.Mints@oktet.ru>
12 *
13 *  Modifications to support UTF-8 in the file system are
14 *  Copyright (c) 2013 embedded brains GmbH.
15 *
16 *  The license and distribution terms for this file may be
17 *  found in the file LICENSE in this distribution or at
18 *  http://www.rtems.org/license/LICENSE.
19 */
20#ifdef HAVE_CONFIG_H
21#include "config.h"
22#endif
23
24#include <ctype.h>
25#include <stdlib.h>
26#include <unistd.h>
27#include <errno.h>
28#include <rtems/libio_.h>
29#include <sys/types.h>
30#include <sys/stat.h>
31
32#include <dirent.h>
33
34#include "fat.h"
35#include "fat_fat_operations.h"
36#include "fat_file.h"
37
38#include "msdos.h"
39
40
41
42/*  msdos_dir_read --
43 *      This routine will read the next directory entry based on the directory
44 *      offset. The offset should be equal to -n- time the size of an
45 *      individual dirent structure. If n is not an integer multiple of the
46 *      sizeof a dirent structure, an integer division will be performed to
47 *      determine directory entry that will be returned in the buffer. Count
48 *      should reflect -m- times the sizeof dirent bytes to be placed in the
49 *      buffer.
50 *      If there are not -m- dirent elements from the current directory
51 *      position to the end of the exisiting file, the remaining entries will
52 *      be placed in the buffer and the returned value will be equal to
53 *      -m actual- times the size of a directory entry.
54 *
55 * PARAMETERS:
56 *     iop    - file control block
57 *     buffer - buffer provided by user
58 *     count  - count of bytes to read
59 *
60 * RETURNS:
61 *     the number of bytes read on success, or -1 if error occured (errno
62 *     set apropriately).
63 */
64ssize_t
65msdos_dir_read(rtems_libio_t *iop, void *buffer, size_t count)
66{
67    int                rc = RC_OK;
68    int                eno = 0;
69    msdos_fs_info_t   *fs_info = iop->pathinfo.mt_entry->fs_info;
70    rtems_dosfs_convert_control *converter = fs_info->converter;
71    const rtems_dosfs_convert_handler *convert_handler = converter->handler;
72    fat_file_fd_t     *fat_fd = iop->pathinfo.node_access;
73    fat_file_fd_t     *tmp_fat_fd = NULL;
74    struct dirent      tmp_dirent;
75    size_t             lfn_len = 0;
76    uint16_t          *lfn_buf = converter->buffer.data;
77    char              *sfn_buf = converter->buffer.data;
78    const size_t       buf_size = converter->buffer.size;
79    uint32_t           start = 0;
80    ssize_t            ret = 0;
81    ssize_t            cmpltd = 0;
82    uint32_t           j = 0, i = 0;
83    uint32_t           bts2rd = 0;
84    uint32_t           cur_cln = 0;
85    uint32_t           lfn_start = FAT_FILE_SHORT_NAME;
86    uint8_t            lfn_checksum = 0;
87    int                lfn_entries = 0;
88    bool               is_first_entry;
89
90    msdos_fs_lock(fs_info);
91
92    /*
93     * cast start and count - protect against using sizes that are not exact
94     * multiples of the -dirent- size. These could result in unexpected
95     * results
96     */
97    start = iop->offset / sizeof(struct dirent);
98    count = (count / sizeof(struct dirent)) * sizeof(struct dirent);
99
100    /*
101     * optimization: we know that root directory for FAT12/16 volumes is
102     * sequential set of sectors and any cluster is sequential set of sectors
103     * too, so read such set of sectors is quick operation for low-level IO
104     * layer.
105     */
106    bts2rd = (FAT_FD_OF_ROOT_DIR(fat_fd) &&
107             (fs_info->fat.vol.type & (FAT_FAT12 | FAT_FAT16))) ?
108             fat_fd->fat_file_size                              :
109             fs_info->fat.vol.bpc;
110
111    while (count > 0 && cmpltd >= 0)
112    {
113        /*
114         * fat-file is already opened by open call, so read it
115         * Always read directory fat-file from the beggining because of MSDOS
116         * directories feature :( - we should count elements currently
117         * present in the directory because there may be holes :)
118         */
119        ret = fat_file_read(&fs_info->fat, fat_fd, (j * bts2rd),
120                            bts2rd, fs_info->cl_buf);
121        if (ret < MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE)
122        {
123            msdos_fs_unlock(fs_info);
124            rtems_set_errno_and_return_minus_one(EIO);
125        }
126
127        for (i = 0; i < ret && cmpltd >= 0; i += MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE)
128        {
129            char* entry = (char*) fs_info->cl_buf + i;
130
131            /*
132             * Is this directory from here on empty ?
133             */
134            if ((*MSDOS_DIR_ENTRY_TYPE(entry)) ==
135                MSDOS_THIS_DIR_ENTRY_AND_REST_EMPTY)
136            {
137                msdos_fs_unlock(fs_info);
138                return cmpltd;
139            }
140
141            /* Is the directory entry empty */
142            if ((*MSDOS_DIR_ENTRY_TYPE(entry)) == MSDOS_THIS_DIR_ENTRY_EMPTY)
143                continue;
144
145            /* Is the directory entry empty a volume label */
146            if (((*MSDOS_DIR_ATTR(entry)) & MSDOS_ATTR_VOLUME_ID) &&
147                ((*MSDOS_DIR_ATTR(entry) & MSDOS_ATTR_LFN_MASK) != MSDOS_ATTR_LFN))
148                continue;
149
150            /*
151             * Check the attribute to see if the entry is for a long file
152             * name.
153             */
154            if ((*MSDOS_DIR_ATTR(entry) & MSDOS_ATTR_LFN_MASK) ==
155                MSDOS_ATTR_LFN)
156            {
157                int offset_lfn;
158
159                /*
160                 * Is this is the first entry of a LFN ?
161                 */
162                if (lfn_start == FAT_FILE_SHORT_NAME)
163                {
164                    is_first_entry = true;
165                    /*
166                     * The first entry must have the last long entry flag set.
167                     */
168                    if ((*MSDOS_DIR_ENTRY_TYPE(entry) &
169                         MSDOS_LAST_LONG_ENTRY) == 0)
170                        continue;
171
172                    /*
173                     * Remember the start location of the long file name.
174                     */
175                    lfn_start =
176                      ((j * bts2rd) + i) / MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE;
177
178                    /*
179                     * Get the number of entries so we can count down and
180                     * also the checksum of the short entry.
181                     */
182                    lfn_entries = (*MSDOS_DIR_ENTRY_TYPE(entry) &
183                                   MSDOS_LAST_LONG_ENTRY_MASK);
184                    lfn_len = 0;
185                    lfn_checksum = *MSDOS_DIR_LFN_CHECKSUM(entry);
186                    memset (tmp_dirent.d_name, 0, sizeof(tmp_dirent.d_name));
187                }
188                else
189                    is_first_entry = false;
190
191                /*
192                 * If the entry number or the check sum do not match
193                 * forget this series of long directory entries. These could
194                 * be orphaned entries depending on the history of the
195                 * disk.
196                 */
197                if ((lfn_entries != (*MSDOS_DIR_ENTRY_TYPE(entry) &
198                                     MSDOS_LAST_LONG_ENTRY_MASK)) ||
199                    (lfn_checksum != *MSDOS_DIR_LFN_CHECKSUM(entry)))
200                {
201                    lfn_start = FAT_FILE_SHORT_NAME;
202                    continue;
203                }
204
205                /*
206                 * Extract the file name into the directory entry. The data is
207                 * stored in UNICODE characters (16bit). No translation is
208                 * done for the possibly partial entry.
209                 * Once all entries have been assembled to a UTF-16 file name,
210                 * this file name will get converted to UTF-8.
211                 *
212                 * The DOS maximum length is 255 characters without the
213                 * trailing nul character. We need to range check the length to
214                 * fit in the directory entry name field.
215                 */
216
217                lfn_entries--;
218                offset_lfn = lfn_entries * MSDOS_LFN_LEN_PER_ENTRY;
219                lfn_len += msdos_get_utf16_string_from_long_entry (
220                  entry,
221                  &lfn_buf[offset_lfn],
222                  buf_size - offset_lfn,
223                  is_first_entry
224                );
225            }
226            else
227            {
228                fat_dir_pos_t dir_pos;
229
230                /*
231                 * Skip active entries until get the entry to start from.
232                 */
233                if (start)
234                {
235                    lfn_start = FAT_FILE_SHORT_NAME;
236                    start--;
237                    continue;
238                }
239
240#ifdef DT_DIR
241                if ((*MSDOS_DIR_ATTR(entry)) & MSDOS_ATTR_DIRECTORY)
242                {
243                    tmp_dirent.d_type = DT_DIR;
244                }
245                else
246                {
247                    tmp_dirent.d_type = DT_REG;
248                }
249#endif
250
251                /*
252                 * Move the entry to the return buffer
253                 *
254                 * unfortunately there is no method to extract ino except to
255                 * open fat-file descriptor :( ... so, open it
256                 */
257
258                /* get number of cluster we are working with */
259                rc = fat_file_ioctl(&fs_info->fat, fat_fd, F_CLU_NUM,
260                                    j * bts2rd, &cur_cln);
261                if (rc != RC_OK)
262                {
263                    msdos_fs_unlock(fs_info);
264                    return rc;
265                }
266
267                fat_dir_pos_init(&dir_pos);
268                dir_pos.sname.cln = cur_cln;
269                dir_pos.sname.ofs = i;
270                rc = fat_file_open(&fs_info->fat, &dir_pos, &tmp_fat_fd);
271                if (rc != RC_OK)
272                {
273                    msdos_fs_unlock(fs_info);
274                    return rc;
275                }
276
277                /* fill in dirent structure */
278                /* XXX: from what and in what d_off should be computed ?! */
279                tmp_dirent.d_off = start + cmpltd;
280                tmp_dirent.d_reclen = sizeof(struct dirent);
281                tmp_dirent.d_ino = tmp_fat_fd->ino;
282
283                /*
284                 * If a long file name check if the correct number of entries
285                 * have been found and if the checksum is correct and if it is
286                 * convertable to utf8 string.  If not return the short file
287                 * name.
288                 */
289                if (lfn_start != FAT_FILE_SHORT_NAME)
290                {
291                    if (lfn_entries == 0 &&
292                        lfn_checksum == msdos_lfn_checksum(entry)) {
293                        size_t len = sizeof(tmp_dirent.d_name) - 1;
294
295                        eno = (*convert_handler->utf16_to_utf8) (
296                            converter,
297                            lfn_buf,
298                            lfn_len,
299                            (uint8_t *) &tmp_dirent.d_name[0],
300                            &len);
301                        if (eno == 0) {
302                            tmp_dirent.d_namlen = len;
303                            tmp_dirent.d_name[len] = '\0';
304                        } else {
305                            lfn_start = FAT_FILE_SHORT_NAME;
306                        }
307                    } else {
308                        lfn_start = FAT_FILE_SHORT_NAME;
309                    }
310                }
311
312                if (lfn_start == FAT_FILE_SHORT_NAME) {
313                    size_t len = sizeof(tmp_dirent.d_name) - 1;
314
315                    /*
316                     * convert dir entry from fixed 8+3 format (without dot)
317                     * to 0..8 + 1dot + 0..3 format
318                     */
319                    tmp_dirent.d_namlen = msdos_format_dirent_with_dot(
320                        sfn_buf, entry); /* src text */
321                    eno = (*convert_handler->codepage_to_utf8) (
322                        converter,
323                        sfn_buf,
324                        tmp_dirent.d_namlen,
325                        (uint8_t *) &tmp_dirent.d_name[0],
326                        &len);
327                    if ( 0 == eno ) {
328                      tmp_dirent.d_namlen = len;
329                      tmp_dirent.d_name[len] = '\0';
330                    } else {
331                        cmpltd = -1;
332                        errno  = eno;
333                    }
334                }
335
336                if ( cmpltd >= 0 ) {
337                    memcpy(buffer + cmpltd, &tmp_dirent, sizeof(struct dirent));
338
339                    iop->offset = iop->offset + sizeof(struct dirent);
340                    cmpltd += (sizeof(struct dirent));
341                    count -= (sizeof(struct dirent));
342
343                    /* inode number extracted, close fat-file */
344                    rc = fat_file_close(&fs_info->fat, tmp_fat_fd);
345                    if (rc != RC_OK)
346                    {
347                        msdos_fs_unlock(fs_info);
348                        return rc;
349                    }
350                }
351            }
352
353            if (count <= 0)
354                break;
355        }
356        j++;
357    }
358
359    msdos_fs_unlock(fs_info);
360    return cmpltd;
361}
362
363/* msdos_dir_write --
364 *     no write for directory
365 */
366
367/* msdos_dir_stat --
368 *
369 * This routine will obtain the following information concerning the current
370 * directory:
371 *     st_dev      device id
372 *     st_ino      node serial number :)
373 *     st_mode     mode extracted from the node
374 *     st_size     total size in bytes
375 *     st_blksize  blocksize for filesystem I/O
376 *     st_blocks   number of blocks allocated
377 *     stat_mtime  time of last modification
378 *
379 * PARAMETERS:
380 *     loc - this directory
381 *     buf - stat buffer provided by user
382 *
383 * RETURNS:
384 *     RC_OK and filled stat buffer on success, or -1 if error occured (errno
385 *     set apropriately).
386 */
387int
388msdos_dir_stat(
389    const rtems_filesystem_location_info_t *loc,
390    struct stat *buf
391)
392{
393    msdos_fs_info_t   *fs_info = loc->mt_entry->fs_info;
394    fat_file_fd_t     *fat_fd = loc->node_access;
395
396    msdos_fs_lock(fs_info);
397
398    buf->st_dev = fs_info->fat.vol.dev;
399    buf->st_ino = fat_fd->ino;
400    buf->st_mode  = S_IFDIR | S_IRWXU | S_IRWXG | S_IRWXO;
401    buf->st_rdev = 0ll;
402    buf->st_size = fat_fd->fat_file_size;
403    buf->st_blocks = fat_fd->fat_file_size >> FAT_SECTOR512_BITS;
404    buf->st_blksize = fs_info->fat.vol.bps;
405    buf->st_atime = fat_fd->mtime;
406    buf->st_ctime = fat_fd->ctime;
407    buf->st_mtime = fat_fd->mtime;
408
409    msdos_fs_unlock(fs_info);
410    return RC_OK;
411}
412
413/* msdos_dir_truncate --
414 *     No truncate for directory.
415 *
416 * PARAMETERS:
417 *
418 * RETURNS:
419 *
420 */
Note: See TracBrowser for help on using the repository browser.