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

4.115
Last change on this file since 796967c was 796967c, checked in by Sebastian Huber <sebastian.huber@…>, on 02/28/12 at 16:19:49

libblock: Change bdbuf API

The functions

o rtems_bdbuf_get(),
o rtems_bdbuf_read(),
o rtems_bdbuf_syncdev(), and
o rtems_bdbuf_purge_dev(),

use now the disk device instead of the device identifier. This makes
bdbuf independent of rtems_disk_obtain() and rtems_disk_release(). It
is the responsiblity of the file system to obtain the disk device. This
also reduces the overhead to get a buffer.

The key for the AVL tree uses now the disk device instead of the device
identifier. The pointer is interpreted as an unsigned integer. This
reduces the memory overhead and makes the comparison operation a bit
faster.

Removed function rtems_bdbuf_purge_major(). This function was too
destructive and could have unpredictable side effects.

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