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

5
Last change on this file since ab96aec was ab96aec, checked in by Sebastian Huber <sebastian.huber@…>, on 08/04/18 at 08:56:46

dosfs: Avoid deprecated routine

Update #3358.

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