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

4.115
Last change on this file since d883ce2 was d883ce2, checked in by Mathew Kallada <matkallada@…>, on 12/20/12 at 14:22:52

libfs: Doxygen Enhancement Task #6"

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