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

4.11
Last change on this file since ad3a744 was a7eaaae8, checked in by Sebastian Huber <sebastian.huber@…>, on 10/20/14 at 07:33:34

dosfs: Support ctime and mtime

Implement ctime and mtime updates according to POSIX. The ctime is
mapped to the FAT create time and date. The mtime is mapped to the FAT
last modified time and date. For the atime use the mtime for
simplicity.

  • Property mode set to 100644
File size: 14.8 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    rtems_status_code  sc = RTEMS_SUCCESSFUL;
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             tmp_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    size_t             string_size = sizeof(tmp_dirent.d_name);
89    bool               is_first_entry;
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    sc = rtems_semaphore_obtain(fs_info->vol_sema, RTEMS_WAIT,
111                                MSDOS_VOLUME_SEMAPHORE_TIMEOUT);
112    if (sc != RTEMS_SUCCESSFUL)
113        rtems_set_errno_and_return_minus_one(EIO);
114
115    while (count > 0 && cmpltd >= 0)
116    {
117        /*
118         * fat-file is already opened by open call, so read it
119         * Always read directory fat-file from the beggining because of MSDOS
120         * directories feature :( - we should count elements currently
121         * present in the directory because there may be holes :)
122         */
123        ret = fat_file_read(&fs_info->fat, fat_fd, (j * bts2rd),
124                            bts2rd, fs_info->cl_buf);
125        if (ret < MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE)
126        {
127            rtems_semaphore_release(fs_info->vol_sema);
128            rtems_set_errno_and_return_minus_one(EIO);
129        }
130
131        for (i = 0; i < ret && cmpltd >= 0; i += MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE)
132        {
133            char* entry = (char*) fs_info->cl_buf + i;
134
135            /*
136             * Is this directory from here on empty ?
137             */
138            if ((*MSDOS_DIR_ENTRY_TYPE(entry)) ==
139                MSDOS_THIS_DIR_ENTRY_AND_REST_EMPTY)
140            {
141                rtems_semaphore_release(fs_info->vol_sema);
142                return cmpltd;
143            }
144
145            /* Is the directory entry empty */
146            if ((*MSDOS_DIR_ENTRY_TYPE(entry)) == MSDOS_THIS_DIR_ENTRY_EMPTY)
147                continue;
148
149            /* Is the directory entry empty a volume label */
150            if (((*MSDOS_DIR_ATTR(entry)) & MSDOS_ATTR_VOLUME_ID) &&
151                ((*MSDOS_DIR_ATTR(entry) & MSDOS_ATTR_LFN_MASK) != MSDOS_ATTR_LFN))
152                continue;
153
154            /*
155             * Check the attribute to see if the entry is for a long file
156             * name.
157             */
158            if ((*MSDOS_DIR_ATTR(entry) & MSDOS_ATTR_LFN_MASK) ==
159                MSDOS_ATTR_LFN)
160            {
161                int offset_lfn;
162
163                /*
164                 * Is this is the first entry of a LFN ?
165                 */
166                if (lfn_start == FAT_FILE_SHORT_NAME)
167                {
168                    is_first_entry = true;
169                    /*
170                     * The first entry must have the last long entry flag set.
171                     */
172                    if ((*MSDOS_DIR_ENTRY_TYPE(entry) &
173                         MSDOS_LAST_LONG_ENTRY) == 0)
174                        continue;
175
176                    /*
177                     * Remember the start location of the long file name.
178                     */
179                    lfn_start =
180                      ((j * bts2rd) + i) / MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE;
181
182                    /*
183                     * Get the number of entries so we can count down and
184                     * also the checksum of the short entry.
185                     */
186                    lfn_entries = (*MSDOS_DIR_ENTRY_TYPE(entry) &
187                                   MSDOS_LAST_LONG_ENTRY_MASK);
188                    tmp_lfn_len = 0;
189                    lfn_checksum = *MSDOS_DIR_LFN_CHECKSUM(entry);
190                    memset (tmp_dirent.d_name, 0, sizeof(tmp_dirent.d_name));
191                }
192                else
193                    is_first_entry = false;
194
195                /*
196                 * If the entry number or the check sum do not match
197                 * forget this series of long directory entries. These could
198                 * be orphaned entries depending on the history of the
199                 * disk.
200                 */
201                if ((lfn_entries != (*MSDOS_DIR_ENTRY_TYPE(entry) &
202                                     MSDOS_LAST_LONG_ENTRY_MASK)) ||
203                    (lfn_checksum != *MSDOS_DIR_LFN_CHECKSUM(entry)))
204                {
205                    lfn_start = FAT_FILE_SHORT_NAME;
206                    continue;
207                }
208
209                /*
210                 * Extract the file name into the directory entry. The data is
211                 * stored in UNICODE characters (16bit). No translation is
212                 * done for the possibly partial entry.
213                 * Once all entries have been assembled to a UTF-16 file name,
214                 * this file name will get converted to UTF-8.
215                 *
216                 * The DOS maximum length is 255 characters without the
217                 * trailing nul character. We need to range check the length to
218                 * fit in the directory entry name field.
219                 */
220
221                lfn_entries--;
222                offset_lfn = lfn_entries * MSDOS_LFN_LEN_PER_ENTRY;
223                tmp_lfn_len += msdos_get_utf16_string_from_long_entry (
224                  entry,
225                  &lfn_buf[offset_lfn],
226                  buf_size - offset_lfn,
227                  is_first_entry
228                );
229            }
230            else
231            {
232                fat_dir_pos_t dir_pos;
233
234                /*
235                 * Skip active entries until get the entry to start from.
236                 */
237                if (start)
238                {
239                    lfn_start = FAT_FILE_SHORT_NAME;
240                    start--;
241                    continue;
242                }
243
244                /*
245                 * Move the entry to the return buffer
246                 *
247                 * unfortunately there is no method to extract ino except to
248                 * open fat-file descriptor :( ... so, open it
249                 */
250
251                /* get number of cluster we are working with */
252                rc = fat_file_ioctl(&fs_info->fat, fat_fd, F_CLU_NUM,
253                                    j * bts2rd, &cur_cln);
254                if (rc != RC_OK)
255                {
256                    rtems_semaphore_release(fs_info->vol_sema);
257                    return rc;
258                }
259
260                fat_dir_pos_init(&dir_pos);
261                dir_pos.sname.cln = cur_cln;
262                dir_pos.sname.ofs = i;
263                rc = fat_file_open(&fs_info->fat, &dir_pos, &tmp_fat_fd);
264                if (rc != RC_OK)
265                {
266                    rtems_semaphore_release(fs_info->vol_sema);
267                    return rc;
268                }
269
270                /* fill in dirent structure */
271                /* XXX: from what and in what d_off should be computed ?! */
272                tmp_dirent.d_off = start + cmpltd;
273                tmp_dirent.d_reclen = sizeof(struct dirent);
274                tmp_dirent.d_ino = tmp_fat_fd->ino;
275
276                /*
277                 * If a long file name check if the correct number of entries
278                 * have been found and if the checksum is correct and if it is
279                 * convertable to utf8 string.  If not return the short file
280                 * name.
281                 */
282                if (lfn_start != FAT_FILE_SHORT_NAME)
283                {
284                    uint8_t  cs = 0;
285                    uint8_t* p = (uint8_t*) entry;
286                    int      i;
287
288                    for (i = 0; i < 11; i++, p++)
289                        cs = ((cs & 1) ? 0x80 : 0) + (cs >> 1) + *p;
290
291                    if (lfn_entries || (lfn_checksum != cs))
292                        lfn_start = FAT_FILE_SHORT_NAME;
293
294                    eno = (*convert_handler->utf16_to_utf8) (
295                        converter,
296                        lfn_buf,
297                        tmp_lfn_len,
298                        (uint8_t*)(&tmp_dirent.d_name[0]),
299                        &string_size);
300                    if (eno == 0) {
301                      tmp_dirent.d_namlen                    = string_size;
302                      tmp_dirent.d_name[tmp_dirent.d_namlen] = '\0';
303                    }
304                    else {
305                        lfn_start = FAT_FILE_SHORT_NAME;
306                    }
307                }
308
309                if (lfn_start == FAT_FILE_SHORT_NAME)
310                {
311                    /*
312                     * convert dir entry from fixed 8+3 format (without dot)
313                     * to 0..8 + 1dot + 0..3 format
314                     */
315                    tmp_dirent.d_namlen = msdos_format_dirent_with_dot(
316                        sfn_buf, entry); /* src text */
317                    eno = (*convert_handler->codepage_to_utf8) (
318                        converter,
319                        sfn_buf,
320                        tmp_dirent.d_namlen,
321                        (uint8_t*)(&tmp_dirent.d_name[0]),
322                        &string_size);
323                    if ( 0 == eno ) {
324                      tmp_dirent.d_namlen                    = string_size;
325                      tmp_dirent.d_name[tmp_dirent.d_namlen] = '\0';
326                    }
327                    else {
328                        cmpltd = -1;
329                        errno  = eno;
330                    }
331                }
332
333                if ( cmpltd >= 0 ) {
334                    memcpy(buffer + cmpltd, &tmp_dirent, sizeof(struct dirent));
335
336                    iop->offset = iop->offset + sizeof(struct dirent);
337                    cmpltd += (sizeof(struct dirent));
338                    count -= (sizeof(struct dirent));
339
340                    /* inode number extracted, close fat-file */
341                    rc = fat_file_close(&fs_info->fat, tmp_fat_fd);
342                    if (rc != RC_OK)
343                    {
344                        rtems_semaphore_release(fs_info->vol_sema);
345                        return rc;
346                    }
347                }
348            }
349
350            if (count <= 0)
351                break;
352        }
353        j++;
354    }
355
356    rtems_semaphore_release(fs_info->vol_sema);
357    return cmpltd;
358}
359
360/* msdos_dir_write --
361 *     no write for directory
362 */
363
364/* msdos_dir_stat --
365 *
366 * This routine will obtain the following information concerning the current
367 * directory:
368 *     st_dev      device id
369 *     st_ino      node serial number :)
370 *     st_mode     mode extracted from the node
371 *     st_size     total size in bytes
372 *     st_blksize  blocksize for filesystem I/O
373 *     st_blocks   number of blocks allocated
374 *     stat_mtime  time of last modification
375 *
376 * PARAMETERS:
377 *     loc - this directory
378 *     buf - stat buffer provided by user
379 *
380 * RETURNS:
381 *     RC_OK and filled stat buffer on success, or -1 if error occured (errno
382 *     set apropriately).
383 */
384int
385msdos_dir_stat(
386    const rtems_filesystem_location_info_t *loc,
387    struct stat *buf
388)
389{
390    rtems_status_code  sc = RTEMS_SUCCESSFUL;
391    msdos_fs_info_t   *fs_info = loc->mt_entry->fs_info;
392    fat_file_fd_t     *fat_fd = loc->node_access;
393
394    sc = rtems_semaphore_obtain(fs_info->vol_sema, RTEMS_WAIT,
395                                MSDOS_VOLUME_SEMAPHORE_TIMEOUT);
396    if (sc != RTEMS_SUCCESSFUL)
397        rtems_set_errno_and_return_minus_one(EIO);
398
399    buf->st_dev = rtems_disk_get_device_identifier(fs_info->fat.vol.dd);
400    buf->st_ino = fat_fd->ino;
401    buf->st_mode  = S_IFDIR | S_IRWXU | S_IRWXG | S_IRWXO;
402    buf->st_rdev = 0ll;
403    buf->st_size = fat_fd->fat_file_size;
404    buf->st_blocks = fat_fd->fat_file_size >> FAT_SECTOR512_BITS;
405    buf->st_blksize = fs_info->fat.vol.bps;
406    buf->st_atime = fat_fd->mtime;
407    buf->st_ctime = fat_fd->ctime;
408    buf->st_mtime = fat_fd->mtime;
409
410    rtems_semaphore_release(fs_info->vol_sema);
411    return RC_OK;
412}
413
414/* msdos_dir_truncate --
415 *     No truncate for directory.
416 *
417 * PARAMETERS:
418 *
419 * RETURNS:
420 *
421 */
Note: See TracBrowser for help on using the repository browser.