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

4.115
Last change on this file since d61b0a5 was d61b0a5, checked in by Sebastian Huber <sebastian.huber@…>, on 05/07/12 at 14:15:57

Filesystem: PR1871: Fix O_APPEND

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