source: rtems/cpukit/libfs/src/dosfs/msdos_misc.c @ a2c204eb

5
Last change on this file since a2c204eb was a2c204eb, checked in by Sebastian Huber <sebastian.huber@…>, on 09/06/17 at 11:58:28

dosfs: Fix find name next entry preparation

Update #2964.

  • Property mode set to 100644
File size: 65.0 KB
RevLine 
[d883ce2]1/**
2 * @file
[f36a7bfc]3 *
[d883ce2]4 * @brief Miscellaneous Routines Implementation for MSDOS FileSystem
5 * @ingroup libfs
6 */
7
8/*
[f36a7bfc]9 *  Copyright (C) 2001 OKTET Ltd., St.-Petersburg, Russia
10 *  Author: Eugeny S. Mints <Eugeny.Mints@oktet.ru>
11 *
[d2e0bb3]12 *  Modifications to support UTF-8 in the file system are
13 *  Copyright (c) 2013 embedded brains GmbH.
14 *
[f36a7bfc]15 *  The license and distribution terms for this file may be
16 *  found in the file LICENSE in this distribution or at
[c499856]17 *  http://www.rtems.org/license/LICENSE.
[f36a7bfc]18 */
19
[3899a537]20#define MSDOS_TRACE 1
21
[f36a7bfc]22#if HAVE_CONFIG_H
23#include "config.h"
24#endif
25
26#include <stdlib.h>
[07d6fd5]27#include <ctype.h>
[f36a7bfc]28#include <sys/time.h>
29#include <unistd.h>
30#include <string.h>
31#include <assert.h>
[d2e0bb3]32#include <errno.h>
33#include <inttypes.h>
[f36a7bfc]34#include <rtems/libio_.h>
35
36#include "fat.h"
37#include "fat_fat_operations.h"
38#include "fat_file.h"
39
40#include "msdos.h"
41
[07d6fd5]42
43#include <stdio.h>
44
[83a4cbb]45#define MSDOS_LFN_ENTRY_SIZE \
46  (MSDOS_LFN_LEN_PER_ENTRY * MSDOS_NAME_LFN_BYTES_PER_CHAR)
47
48#define MSDOS_LFN_ENTRY_SIZE_UTF8 \
49  ((MSDOS_LFN_LEN_PER_ENTRY + 1 ) * MSDOS_NAME_LFN_BYTES_PER_CHAR \
50    * MSDOS_NAME_MAX_UTF8_BYTES_PER_CHAR)
51
[07d6fd5]52/*
[a0f3ce2]53 * External strings. Saves space this way.
[07d6fd5]54 */
[85b9e7f]55const char *const MSDOS_DOT_NAME    = ".          ";
56const char *const MSDOS_DOTDOT_NAME = "..         ";
[07d6fd5]57
[3efe749]58uint8_t
59msdos_lfn_checksum(const void *entry)
60{
61    const uint8_t *name;
62    uint8_t        cs;
63    int            i;
64
65    name = (const uint8_t *) MSDOS_DIR_NAME(entry);
66    cs = 0;
67
68    for (i = 0; i < MSDOS_SHORT_NAME_LEN; ++i) {
69        cs = ((cs & 1) ? 0x80 : 0) + (cs >> 1) + name[i];
70    }
71
72    return cs;
73}
74
[07d6fd5]75/* msdos_is_valid_name_char --
76 *     Routine to check the character in a file or directory name.
77 *     The characters support in the short file name are letters,
78 *     digits, or characters with code points values greater than
79 *     127 (not sure what this last is) plus the following special
80 *     characters "$%'-_@~`!(){}^#&". The must be uppercase.
81 *
82 *     The following 6 characters are allowed in a long names,
83 *     " +,;=[]" including a space and lower case letters.
84 *
85 * PARAMETERS:
86 *     ch        - character to check.
87 *
88 * RETURNS:
89 *     MSDOS_NAME_INVALID - Not valid in a long or short name.
90 *     MSDOS_NAME_SHORT   - Valid in a short name or long name.
91 *     MSDOS_NAME_LONG    - Valid in a long name only.
92 *
93 */
94static msdos_name_type_t
95msdos_is_valid_name_char(const char ch)
96{
97    if (strchr(" +,;=[]", ch) != NULL)
98        return MSDOS_NAME_LONG;
99
[bab5c5fa]100    if ((ch == '.') || isalnum((unsigned char)ch) ||
[d2e0bb3]101        (strchr("$%'-_@~`!(){}^#&", ch) != NULL) || (unsigned char) ch > 127)
[07d6fd5]102        return MSDOS_NAME_SHORT;
[0a7278e]103
[07d6fd5]104    return MSDOS_NAME_INVALID;
105}
106
107/* msdos_short_hex_number --
108 *     Routine to set the hex number in the SFN.
109 *
110 * PARAMETERS:
111 *     name      - name to change
112 *     num       - number to set
113 *
114 * RETURNS:
115 *     nothing
116 *
117 */
118static void
[b1daf0f]119msdos_short_name_hex(char* sfn, uint32_t num)
[07d6fd5]120{
121    static const char* hex = "0123456789ABCDEF";
122    char* c = MSDOS_DIR_NAME(sfn);
123    int   i;
[3ae5950]124    for (i = 0; i < 2; i++, c++)
[07d6fd5]125      if ((*c == ' ') || (*c == '.'))
[3ae5950]126        *c = '_';
[07d6fd5]127    for (i = 0; i < 4; i++, c++)
128      *c = hex[(num >> ((3 - i) * 4)) & 0xf];
[3ae5950]129    *c++ = '~';
[43d6a28]130    *c   = '1';
[07d6fd5]131}
132
133/* msdos_name_type --
134 *     Routine the type of file name.
135 *
136 * PARAMETERS:
137 *     name      - name to check
138 *
139 * RETURNS:
140 *     true the name is long, else the name is short.
141 *
142 */
[8f77f8f]143#define MSDOS_NAME_TYPE_PRINT 0
[07d6fd5]144static msdos_name_type_t
145msdos_name_type(const char *name, int name_len)
146{
147    bool lowercase = false;
148    bool uppercase = false;
[1b3f7e92]149    int  dot_at = -1;
[07d6fd5]150    int  count = 0;
151
152    while (*name && (count < name_len))
153    {
[1b3f7e92]154        bool is_dot = *name == '.';
[07d6fd5]155        msdos_name_type_t type = msdos_is_valid_name_char(*name);
156
[8f77f8f]157#if MSDOS_NAME_TYPE_PRINT
158        printf ("MSDOS_NAME_TYPE: c:%02x type:%d\n", *name, type);
159#endif
[66b8047]160
[07d6fd5]161        if ((type == MSDOS_NAME_INVALID) || (type == MSDOS_NAME_LONG))
162            return type;
163
[1b3f7e92]164        if (dot_at >= 0)
[07d6fd5]165        {
[1b3f7e92]166            if (is_dot || ((count - dot_at) > 3))
[8f77f8f]167            {
168#if MSDOS_NAME_TYPE_PRINT
169                printf ("MSDOS_NAME_TYPE: LONG[1]: is_dot:%d, at:%d cnt\n",
170                        is_dot, dot_at, count);
171#endif
[07d6fd5]172                return MSDOS_NAME_LONG;
[8f77f8f]173            }
[07d6fd5]174        }
175        else
176        {
[1b3f7e92]177            if (count == 8 && !is_dot)
[8f77f8f]178            {
179#if MSDOS_NAME_TYPE_PRINT
180                printf ("MSDOS_NAME_TYPE: LONG[2]: is_dot:%d, at:%d cnt\n",
181                        is_dot, dot_at, count);
182#endif
[07d6fd5]183                return MSDOS_NAME_LONG;
[8f77f8f]184            }
[07d6fd5]185        }
186
[1b3f7e92]187        if (is_dot)
[07d6fd5]188            dot_at = count;
189        else if ((*name >= 'A') && (*name <= 'Z'))
190            uppercase = true;
191        else if ((*name >= 'a') && (*name <= 'z'))
192            lowercase = true;
[0a7278e]193
[07d6fd5]194        count++;
195        name++;
196    }
197
198    if (lowercase && uppercase)
[8f77f8f]199    {
200#if MSDOS_NAME_TYPE_PRINT
201        printf ("MSDOS_NAME_TYPE: LONG[3]\n");
202#endif
[07d6fd5]203        return MSDOS_NAME_LONG;
[8f77f8f]204    }
[66b8047]205
[8f77f8f]206#if MSDOS_NAME_TYPE_PRINT
207    printf ("MSDOS_NAME_TYPE: SHORT[1]\n");
208#endif
[07d6fd5]209    return MSDOS_NAME_SHORT;
210}
211
212/* msdos_long_to_short --
[0a7278e]213 *     Routine to creates a short name from a long. Start the end of the
[07d6fd5]214 *
215 * PARAMETERS:
216 *     name      - name to check
217 *
218 * RETURNS:
219 *     true the name is long, else the name is short.
220 *
221 */
[8f77f8f]222#define MSDOS_L2S_PRINT 0
[07d6fd5]223msdos_name_type_t
[d2e0bb3]224msdos_long_to_short(rtems_dosfs_convert_control     *converter,
225                    const char                      *lfn,
226                    int                              lfn_len,
227                    char                            *sfn,
228                    int                              sfn_len)
[07d6fd5]229{
230    msdos_name_type_t type;
[d2e0bb3]231    int               eno = 0;
[07d6fd5]232    int               i;
[d2e0bb3]233    ssize_t           short_filename_length = sfn_len;
234    void             *buffer = converter->buffer.data;
235    size_t            codepage_name_len = converter->buffer.size;
[07d6fd5]236
237    /*
238     * Fill with spaces. This is how a short directory entry is padded.
239     */
240    memset (sfn, ' ', sfn_len);
241
242    /*
243     * Handle '.' and '..' specially.
244     */
245    if ((lfn[0] == '.') && (lfn_len == 1))
246    {
247        sfn[0] = '.';
[8f77f8f]248#if MSDOS_L2S_PRINT
249        printf ("MSDOS_L2S: SHORT[1]: lfn:'%s' SFN:'%s'\n", lfn, sfn);
250#endif
[07d6fd5]251        return MSDOS_NAME_SHORT;
252    }
[0a7278e]253
[07d6fd5]254    if ((lfn[0] == '.') && (lfn[1] == '.') && (lfn_len == 2))
255    {
256        sfn[0] = sfn[1] = '.';
[8f77f8f]257#if MSDOS_L2S_PRINT
258        printf ("MSDOS_L2S: SHORT[2]: lfn:'%s' SFN:'%s'\n", lfn, sfn);
259#endif
[07d6fd5]260        return MSDOS_NAME_SHORT;
261    }
262
263    /*
264     * Filenames with only blanks and dots are not allowed!
265     */
266    for (i = 0; i < lfn_len; i++)
[1b3f7e92]267        if ((lfn[i] != ' ') && (lfn[i] != '.'))
[07d6fd5]268            break;
269
[1b3f7e92]270    if (i == lfn_len)
[8f77f8f]271    {
272#if MSDOS_L2S_PRINT
273        printf ("MSDOS_L2S: INVALID[1]: lfn:'%s' SFN:'%s'\n", lfn, sfn);
274#endif
[07d6fd5]275        return MSDOS_NAME_INVALID;
[8f77f8f]276    }
[07d6fd5]277
278    /*
279     * Is this a short name ?
280     */
[0a7278e]281
[d2e0bb3]282    eno = (*converter->handler->utf8_to_codepage) (
283        converter,
284        (const uint8_t*)&lfn[0],
285        lfn_len,
286        buffer,
287        &codepage_name_len);
288    if (eno == EINVAL)
289    {
290        eno = 0;
291        type = MSDOS_NAME_LONG;
292    }
293    else
294    {
295        type = msdos_name_type (
296            buffer,
297            codepage_name_len);
298    }
[07d6fd5]299
[d2e0bb3]300    if (type != MSDOS_NAME_INVALID)
[8f77f8f]301    {
[d2e0bb3]302        short_filename_length = msdos_filename_utf8_to_short_name_for_save (
303            converter,
304            (const uint8_t*)lfn,
305            lfn_len,
306            sfn,
307            short_filename_length);
308        if (short_filename_length < 0 ) {
309            type = MSDOS_NAME_INVALID;
310        }
[8f77f8f]311#if MSDOS_L2S_PRINT
[d2e0bb3]312        printf ("MSDOS_L2S: TYPE:%d lfn:'%s' SFN:'%s'\n", type, lfn, sfn);
[8f77f8f]313#endif
314    }
[d2e0bb3]315    else
316    {
[8f77f8f]317#if MSDOS_L2S_PRINT
[d2e0bb3]318        printf ("MSDOS_L2S: INVALID[2]: lfn:'%s' SFN:'%s'\n", lfn, sfn);
[8f77f8f]319#endif
[d2e0bb3]320    }
321
[07d6fd5]322    return type;
323}
324
[f36a7bfc]325/* msdos_find_name --
[a5305f6b]326 *     Find the node which correspondes to the name, open fat-file which
327 *     correspondes to the found node and close fat-file which correspondes
[f36a7bfc]328 *     to the node we searched in.
329 *
330 * PARAMETERS:
331 *     parent_loc - parent node description
332 *     name       - name to find
333 *
334 * RETURNS:
[a5305f6b]335 *     RC_OK and updated 'parent_loc' on success, or -1 if error
[f36a7bfc]336 *     occured (errno set apropriately)
337 *
338 */
[a5305f6b]339int
[f36a7bfc]340msdos_find_name(
341    rtems_filesystem_location_info_t *parent_loc,
[07d6fd5]342    const char                       *name,
343    int                               name_len
[f36a7bfc]344    )
345{
[07d6fd5]346    int                rc = RC_OK;
347    msdos_fs_info_t   *fs_info = parent_loc->mt_entry->fs_info;
348    fat_file_fd_t     *fat_fd = NULL;
349    msdos_name_type_t  name_type;
350    fat_dir_pos_t      dir_pos;
351    unsigned short     time_val = 0;
352    unsigned short     date = 0;
353    char               node_entry[MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE];
[a5305f6b]354
[f36a7bfc]355    memset(node_entry, 0, MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE);
[a5305f6b]356
[d2e0bb3]357    name_type = msdos_long_to_short (
358        fs_info->converter,
359        name,
360        name_len,
361        MSDOS_DIR_NAME(node_entry),
362        MSDOS_NAME_MAX);
[07d6fd5]363
[a5305f6b]364    /*
[d2e0bb3]365     * find the node which corresponds to the name in the directory pointed by
[f36a7bfc]366     * 'parent_loc'
367     */
[07d6fd5]368    rc = msdos_get_name_node(parent_loc, false, name, name_len, name_type,
369                             &dir_pos, node_entry);
[f36a7bfc]370    if (rc != RC_OK)
371        return rc;
372
[07d6fd5]373    if (((*MSDOS_DIR_ATTR(node_entry)) & MSDOS_ATTR_VOLUME_ID) ||
374        ((*MSDOS_DIR_ATTR(node_entry) & MSDOS_ATTR_LFN_MASK) == MSDOS_ATTR_LFN))
375        return MSDOS_NAME_NOT_FOUND_ERR;
376
[f36a7bfc]377    /* open fat-file corresponded to the found node */
[c65afce4]378    rc = fat_file_open(&fs_info->fat, &dir_pos, &fat_fd);
[f36a7bfc]379    if (rc != RC_OK)
380        return rc;
[a5305f6b]381
[07d6fd5]382    fat_fd->dir_pos = dir_pos;
[0a7278e]383
[f36a7bfc]384    /*
[07d6fd5]385     * I don't like this if, but: we should do it, or should write new file
[a5305f6b]386     * size and first cluster num to the disk after each write operation
[07d6fd5]387     * (even if one byte is written  - that is TOO slow) because
[a5305f6b]388     * otherwise real values of these fields stored in fat-file descriptor
[07d6fd5]389     * may be accidentally rewritten with wrong values stored on the disk
[f36a7bfc]390     */
391    if (fat_fd->links_num == 1)
392    {
393        fat_fd->cln = MSDOS_EXTRACT_CLUSTER_NUM(node_entry);
[a5305f6b]394
[f36a7bfc]395        time_val = *MSDOS_DIR_WRITE_TIME(node_entry);
396        date = *MSDOS_DIR_WRITE_DATE(node_entry);
[a5305f6b]397
[07d6fd5]398        fat_fd->mtime = msdos_date_dos2unix(CF_LE_W(date), CF_LE_W(time_val));
[a5305f6b]399
[a7eaaae8]400        time_val = *MSDOS_DIR_CRT_TIME(node_entry);
401        date = *MSDOS_DIR_CRT_DATE(node_entry);
402
403        fat_fd->ctime = msdos_date_dos2unix(CF_LE_W(date), CF_LE_W(time_val));
404
[f36a7bfc]405        if ((*MSDOS_DIR_ATTR(node_entry)) & MSDOS_ATTR_DIRECTORY)
406        {
407            fat_fd->fat_file_type = FAT_DIRECTORY;
[665f03a]408            fat_fd->size_limit = MSDOS_MAX_DIR_LENGTH;
[a5305f6b]409
[c65afce4]410            rc = fat_file_size(&fs_info->fat, fat_fd);
[f36a7bfc]411            if (rc != RC_OK)
412            {
[c65afce4]413                fat_file_close(&fs_info->fat, fat_fd);
[f36a7bfc]414                return rc;
415            }
416        }
417        else
418        {
[a5305f6b]419            fat_fd->fat_file_size = CF_LE_L(*MSDOS_DIR_FILE_SIZE(node_entry));
420            fat_fd->fat_file_type = FAT_FILE;
[f36a7bfc]421            fat_fd->size_limit = MSDOS_MAX_FILE_SIZE;
422        }
[a5305f6b]423
[f36a7bfc]424        /* these data is not actual for zero-length fat-file */
425        fat_fd->map.file_cln = 0;
426        fat_fd->map.disk_cln = fat_fd->cln;
[a5305f6b]427
428        if ((fat_fd->fat_file_size != 0) &&
[f36a7bfc]429            (fat_fd->fat_file_size <= fs_info->fat.vol.bpc))
430        {
431            fat_fd->map.last_cln = fat_fd->cln;
432        }
433        else
434        {
435            fat_fd->map.last_cln = FAT_UNDEFINED_VALUE;
436        }
[a5305f6b]437    }
[f36a7bfc]438
439    /* close fat-file corresponded to the node we searched in */
[c65afce4]440    rc = fat_file_close(&fs_info->fat, parent_loc->node_access);
[f36a7bfc]441    if (rc != RC_OK)
442    {
[c65afce4]443        fat_file_close(&fs_info->fat, fat_fd);
[f36a7bfc]444        return rc;
445    }
446
447    /* update node_info_ptr field */
448    parent_loc->node_access = fat_fd;
[a5305f6b]449
[f36a7bfc]450    return rc;
[a5305f6b]451}
[f36a7bfc]452
453/* msdos_get_name_node --
[07d6fd5]454 *     This routine is used in two ways: for a new node creation (a) or for
[f36a7bfc]455 *     search the node which correspondes to the name parameter (b).
[a5305f6b]456 *     In case (a) 'name' should be set up to NULL and 'name_dir_entry' should
457 *     point to initialized 32 bytes structure described a new node.
[f36a7bfc]458 *     In case (b) 'name' should contain a valid string.
459 *
[a5305f6b]460 *     (a): reading fat-file which correspondes to directory we are going to
461 *          create node in. If free slot is found write contents of
462 *          'name_dir_entry' into it. If reach end of fat-file and no free
[f36a7bfc]463 *          slot found, write 32 bytes to the end of fat-file.
464 *
[a5305f6b]465 *     (b): reading fat-file which correspondes to directory and trying to
[f36a7bfc]466 *          find slot with the name field == 'name' parameter
467 *
468 *
469 * PARAMETERS:
470 *     parent_loc     - node description to create node in or to find name in
471 *     name           - NULL or name to find
472 *     paux           - identify a node location on the disk -
[a5305f6b]473 *                      cluster num and offset inside the cluster
[07d6fd5]474 *     short_dir_entry - node to create/placeholder for found node (IN/OUT)
[f36a7bfc]475 *
476 * RETURNS:
[a5305f6b]477 *     RC_OK, filled aux_struct_ptr and name_dir_entry on success, or -1 if
[f36a7bfc]478 *     error occured (errno set apropriately)
479 *
480 */
[e197621]481int
[f36a7bfc]482msdos_get_name_node(
[3b7c123]483    const rtems_filesystem_location_info_t *parent_loc,
484    bool                                    create_node,
485    const char                             *name,
486    int                                     name_len,
487    msdos_name_type_t                       name_type,
488    fat_dir_pos_t                          *dir_pos,
489    char                                   *name_dir_entry
[f36a7bfc]490    )
491{
[e197621]492    int              rc = RC_OK;
[f36a7bfc]493    fat_file_fd_t   *fat_fd = parent_loc->node_access;
[f91bbe64]494    uint32_t         dotdot_cln = 0;
[f36a7bfc]495
[07d6fd5]496    /* find name in fat-file which corresponds to the directory */
497    rc = msdos_find_name_in_fat_file(parent_loc->mt_entry, fat_fd,
[d2e0bb3]498                                     create_node, (const uint8_t*)name, name_len, name_type,
[07d6fd5]499                                     dir_pos, name_dir_entry);
[f36a7bfc]500    if ((rc != RC_OK) && (rc != MSDOS_NAME_NOT_FOUND_ERR))
501        return rc;
[a5305f6b]502
[07d6fd5]503    if (!create_node)
[f36a7bfc]504    {
[07d6fd5]505        /* if we search for valid name and name not found -> return */
506        if (rc == MSDOS_NAME_NOT_FOUND_ERR)
[f36a7bfc]507            return rc;
508
[a5305f6b]509        /*
[07d6fd5]510         * if we have deal with ".." - it is a special case :(((
511         *
512         * Really, we should return cluster num and offset not of ".." slot, but
513         * slot which correspondes to real directory name.
[f36a7bfc]514         */
[07d6fd5]515        if (rc == RC_OK)
[f36a7bfc]516        {
[07d6fd5]517            if (strncmp(name, "..", 2) == 0)
[f36a7bfc]518            {
[07d6fd5]519                dotdot_cln = MSDOS_EXTRACT_CLUSTER_NUM((name_dir_entry));
520
521                /* are we right under root dir ? */
522                if (dotdot_cln == 0)
523                {
524                    /*
525                     * we can relax about first_char field - it never should be
526                     * used for root dir
527                     */
528                    fat_dir_pos_init(dir_pos);
529                    dir_pos->sname.cln = FAT_ROOTDIR_CLUSTER_NUM;
530                }
531                else
532                {
533                    rc =
534                        msdos_get_dotdot_dir_info_cluster_num_and_offset(parent_loc->mt_entry,
535                                                                         dotdot_cln,
536                                                                         dir_pos,
537                                                                         name_dir_entry);
538                    if (rc != RC_OK)
539                        return rc;
540                }
[f36a7bfc]541            }
[a5305f6b]542        }
[f36a7bfc]543    }
544    return rc;
545}
546
547/*
548 * msdos_get_dotdot_dir_info_cluster_num_and_offset
549 *
[a5305f6b]550 * Unfortunately, in general, we cann't work here in fat-file ideologic
551 * (open fat_file "..", get ".." and ".", open "..", find an entry ...)
[f36a7bfc]552 * because if we open
553 * fat-file ".." it may happend that we have two different fat-file
[a5305f6b]554 * descriptors ( for real name of directory and ".." name ) for a single
[f36a7bfc]555 * file  ( cluster num of both pointers to the same cluster )
556 * But...we do it because we protected by semaphore
[a5305f6b]557 *
[f36a7bfc]558 */
559
560/* msdos_get_dotdot_dir_info_cluster_num_and_offset --
[a5305f6b]561 *     Get cluster num and offset not of ".." slot, but slot which correspondes
562 *     to real directory name.
[f36a7bfc]563 *
564 * PARAMETERS:
565 *     mt_entry       - mount table entry
566 *     cln            - data cluster num extracted drom ".." slot
567 *     paux           - identify a node location on the disk -
568 *                      number of cluster and offset inside the cluster
[a5305f6b]569 *     dir_entry      - placeholder for found node
[f36a7bfc]570 *
571 * RETURNS:
572 *     RC_OK, filled 'paux' and 'dir_entry' on success, or -1 if error occured
573 *     (errno set apropriately)
574 *
575 */
576int
577msdos_get_dotdot_dir_info_cluster_num_and_offset(
[a5305f6b]578    rtems_filesystem_mount_table_entry_t *mt_entry,
[f91bbe64]579    uint32_t                              cln,
[07d6fd5]580    fat_dir_pos_t                        *dir_pos,
[f36a7bfc]581    char                                 *dir_entry
582    )
583{
584    int              rc = RC_OK;
585    msdos_fs_info_t *fs_info = mt_entry->fs_info;
586    fat_file_fd_t   *fat_fd = NULL;
[adc829ed]587    char             dot_node[MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE];
588    char             dotdot_node[MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE];
[f91bbe64]589    uint32_t         cl4find = 0;
[d2e0bb3]590    rtems_dosfs_convert_control *converter = fs_info->converter;
[a5305f6b]591
[f36a7bfc]592    /*
593     * open fat-file corresponded to ".."
594     */
[c65afce4]595    rc = fat_file_open(&fs_info->fat, dir_pos, &fat_fd);
[f36a7bfc]596    if (rc != RC_OK)
597        return rc;
[a5305f6b]598
[f36a7bfc]599    fat_fd->cln = cln;
600    fat_fd->fat_file_type = FAT_DIRECTORY;
[665f03a]601    fat_fd->size_limit = MSDOS_MAX_DIR_LENGTH;
[a5305f6b]602
[f36a7bfc]603    fat_fd->map.file_cln = 0;
604    fat_fd->map.disk_cln = fat_fd->cln;
605
[c65afce4]606    rc = fat_file_size(&fs_info->fat, fat_fd);
[f36a7bfc]607    if (rc != RC_OK)
608    {
[c65afce4]609        fat_file_close(&fs_info->fat, fat_fd);
[f36a7bfc]610        return rc;
[a5305f6b]611    }
612
[f36a7bfc]613    /* find "." node in opened directory */
[07d6fd5]614    memset(dot_node, 0, MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE);
[d2e0bb3]615    msdos_long_to_short(
616        converter,
617        ".", 1, dot_node, MSDOS_SHORT_NAME_LEN);
618    rc = msdos_find_name_in_fat_file(mt_entry, fat_fd, false, (const uint8_t*)".", 1,
[07d6fd5]619                                     MSDOS_NAME_SHORT, dir_pos, dot_node);
[a5305f6b]620
[f36a7bfc]621    if (rc != RC_OK)
622    {
[c65afce4]623        fat_file_close(&fs_info->fat, fat_fd);
[f36a7bfc]624        return rc;
625    }
[a5305f6b]626
[f36a7bfc]627    /* find ".." node in opened directory */
[07d6fd5]628    memset(dotdot_node, 0, MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE);
[d2e0bb3]629    msdos_long_to_short(
630        converter,
631        "..", 2, dotdot_node, MSDOS_SHORT_NAME_LEN);
632    rc = msdos_find_name_in_fat_file(mt_entry, fat_fd, false, (const uint8_t*)"..", 2,
[07d6fd5]633                                     MSDOS_NAME_SHORT, dir_pos,
[f36a7bfc]634                                     dotdot_node);
[a5305f6b]635
636    if (rc != RC_OK)
[f36a7bfc]637    {
[c65afce4]638        fat_file_close(&fs_info->fat, fat_fd);
[f36a7bfc]639        return rc;
640    }
641
642    cl4find = MSDOS_EXTRACT_CLUSTER_NUM(dot_node);
[a5305f6b]643
[f36a7bfc]644    /* close fat-file corresponded to ".." directory */
[c65afce4]645    rc = fat_file_close(&fs_info->fat, fat_fd);
[f36a7bfc]646    if ( rc != RC_OK )
647        return rc;
648
649    if ( (MSDOS_EXTRACT_CLUSTER_NUM(dotdot_node)) == 0)
650    {
[a5305f6b]651        /*
652         * we handle root dir for all FAT types in the same way with the
[f36a7bfc]653         * ordinary directories ( through fat_file_* calls )
654         */
[07d6fd5]655        fat_dir_pos_init(dir_pos);
656        dir_pos->sname.cln = FAT_ROOTDIR_CLUSTER_NUM;
[f36a7bfc]657    }
658
659    /* open fat-file corresponded to second ".." */
[c65afce4]660    rc = fat_file_open(&fs_info->fat, dir_pos, &fat_fd);
[f36a7bfc]661    if (rc != RC_OK)
662        return rc;
663
664    if ((MSDOS_EXTRACT_CLUSTER_NUM(dotdot_node)) == 0)
665        fat_fd->cln = fs_info->fat.vol.rdir_cl;
666    else
667        fat_fd->cln = MSDOS_EXTRACT_CLUSTER_NUM(dotdot_node);
668
669    fat_fd->fat_file_type = FAT_DIRECTORY;
[665f03a]670    fat_fd->size_limit = MSDOS_MAX_DIR_LENGTH;
[a5305f6b]671
[f36a7bfc]672    fat_fd->map.file_cln = 0;
673    fat_fd->map.disk_cln = fat_fd->cln;
674
[c65afce4]675    rc = fat_file_size(&fs_info->fat, fat_fd);
[f36a7bfc]676    if (rc != RC_OK)
677    {
[c65afce4]678        fat_file_close(&fs_info->fat, fat_fd);
[f36a7bfc]679        return rc;
[a5305f6b]680    }
681
[f36a7bfc]682    /* in this directory find slot with specified cluster num */
[a5305f6b]683    rc = msdos_find_node_by_cluster_num_in_fat_file(mt_entry, fat_fd, cl4find,
[07d6fd5]684                                                    dir_pos, dir_entry);
[f36a7bfc]685    if (rc != RC_OK)
686    {
[c65afce4]687        fat_file_close(&fs_info->fat, fat_fd);
[f36a7bfc]688        return rc;
689    }
[c65afce4]690    rc = fat_file_close(&fs_info->fat, fat_fd);
[f36a7bfc]691    return rc;
692}
693
694
[a7eaaae8]695/* fat_file_write_time_and_date --
[a5305f6b]696 *     Write last write date and time for a file to the disk (to corresponded
697 *     32bytes node)
[f36a7bfc]698 *
699 * PARAMETERS:
[a7eaaae8]700 *     fs_info  - fat fs info
[f36a7bfc]701 *     fat_fd   - fat-file descriptor
702 *
703 * RETURNS:
704 *     RC_OK on success, or -1 if error occured (errno set apropriately).
705 *
706 */
707int
[a7eaaae8]708fat_file_write_time_and_date(
709    fat_fs_info_t                        *fs_info,
[f36a7bfc]710    fat_file_fd_t                        *fat_fd
711    )
712{
[a7eaaae8]713    int              rc = RC_OK;
714    ssize_t          ret;
[5ac15a5]715    uint16_t         time_val;
716    uint16_t         date;
[f91bbe64]717    uint32_t         sec = 0;
718    uint32_t         byte = 0;
[a5305f6b]719
720    /*
[42a22f08]721     * calculate input for fat_sector_write: convert (cluster num, offset) to
[f36a7bfc]722     * (sector num, new offset)
723     */
[a7eaaae8]724    sec = fat_cluster_num_to_sector_num(fs_info, fat_fd->dir_pos.sname.cln);
725    sec += (fat_fd->dir_pos.sname.ofs >> fs_info->vol.sec_log2);
[f36a7bfc]726    /* byte points to start of 32bytes structure */
[a7eaaae8]727    byte = fat_fd->dir_pos.sname.ofs & (fs_info->vol.bps - 1);
728
729    msdos_date_unix2dos(fat_fd->mtime, &date, &time_val);
[a5305f6b]730
[f36a7bfc]731    time_val = CT_LE_W(time_val);
[a7eaaae8]732    ret = fat_sector_write(fs_info, sec, byte + MSDOS_FILE_WTIME_OFFSET,
733                           2, (char *)(&time_val));
734    if ( ret < 0 )
735        rc = -1;
736
[f36a7bfc]737    date = CT_LE_W(date);
[a7eaaae8]738    ret = fat_sector_write(fs_info, sec, byte + MSDOS_FILE_WDATE_OFFSET,
739                           2, (char *)(&date));
740    if ( ret < 0 )
741        rc = -1;
[f36a7bfc]742
[a7eaaae8]743    ret = fat_sector_write(fs_info, sec, byte + MSDOS_FILE_ADATE_OFFSET,
744                           2, (char *)(&date));
745    if ( ret < 0 )
746        rc = -1;
[f36a7bfc]747
[a7eaaae8]748    msdos_date_unix2dos(fat_fd->ctime, &date, &time_val);
749
750    time_val = CT_LE_W(time_val);
751    ret = fat_sector_write(fs_info, sec, byte + MSDOS_FILE_CTIME_OFFSET,
752                           2, (char *)(&time_val));
753    if ( ret < 0 )
754        rc = -1;
755
756    date = CT_LE_W(date);
757    ret = fat_sector_write(fs_info, sec, byte + MSDOS_FILE_CDATE_OFFSET,
758                           2, (char *)(&date));
759    if ( ret < 0 )
760        rc = -1;
761
762    return rc;
[f36a7bfc]763}
764
[a7eaaae8]765/* fat_set_first_cluster_num --
[a5305f6b]766 *     Write number of first cluster of the file to the disk (to corresponded
767 *     32bytes slot)
[f36a7bfc]768 *
769 * PARAMETERS:
[a7eaaae8]770 *     fs_info  - fat fs info
[f36a7bfc]771 *     fat_fd   - fat-file descriptor
772 *
773 * RETURNS:
774 *     RC_OK on success, or -1 if error occured
775 *
776 */
777int
[a7eaaae8]778fat_file_write_first_cluster_num(
779    fat_fs_info_t                        *fs_info,
[f36a7bfc]780    fat_file_fd_t                        *fat_fd
781    )
782{
783    ssize_t          ret1 = 0, ret2 = 0;
[f91bbe64]784    uint32_t         new_cln = fat_fd->cln;
785    uint16_t         le_cl_low = 0;
[a5305f6b]786    uint16_t         le_cl_hi = 0;
[f91bbe64]787    uint32_t         sec = 0;
788    uint32_t         byte = 0;
[f36a7bfc]789
[a5305f6b]790    /*
[42a22f08]791     * calculate input for fat_sector_write: convert (cluster num, offset) to
[f36a7bfc]792     * (sector num, new offset)
793     */
[a7eaaae8]794    sec = fat_cluster_num_to_sector_num(fs_info, fat_fd->dir_pos.sname.cln);
795    sec += (fat_fd->dir_pos.sname.ofs >> fs_info->vol.sec_log2);
[f36a7bfc]796    /* byte from points to start of 32bytes structure */
[a7eaaae8]797    byte = fat_fd->dir_pos.sname.ofs & (fs_info->vol.bps - 1);
[a5305f6b]798
[f91bbe64]799    le_cl_low = CT_LE_W((uint16_t  )(new_cln & 0x0000FFFF));
[a7eaaae8]800    ret1 = fat_sector_write(fs_info, sec,
[f36a7bfc]801                            byte + MSDOS_FIRST_CLUSTER_LOW_OFFSET, 2,
802                            (char *)(&le_cl_low));
[f91bbe64]803    le_cl_hi = CT_LE_W((uint16_t  )((new_cln & 0xFFFF0000) >> 16));
[a7eaaae8]804    ret2 = fat_sector_write(fs_info, sec,
[f36a7bfc]805                            byte + MSDOS_FIRST_CLUSTER_HI_OFFSET, 2,
806                            (char *)(&le_cl_hi));
807    if ( (ret1 < 0) || (ret2 < 0) )
808        return -1;
809
810    return RC_OK;
811}
812
813
[a7eaaae8]814/* fat_set_file size --
[a5305f6b]815 *     Write file size of the file to the disk (to corresponded 32bytes slot)
[f36a7bfc]816 *
817 * PARAMETERS:
[a7eaaae8]818 *     fs_info  - fat fs info
[f36a7bfc]819 *     fat_fd   - fat-file descriptor
820 *
821 * RETURNS:
822 *     RC_OK on success, or -1 if error occured (errno set apropriately).
823 *
824 */
825int
[a7eaaae8]826fat_file_write_file_size(
827    fat_fs_info_t                        *fs_info,
[f36a7bfc]828    fat_file_fd_t                        *fat_fd
829    )
830{
831    ssize_t          ret = 0;
[f91bbe64]832    uint32_t         le_new_length = 0;
833    uint32_t         sec = 0;
834    uint32_t         byte = 0;
[f36a7bfc]835
[a7eaaae8]836    sec = fat_cluster_num_to_sector_num(fs_info, fat_fd->dir_pos.sname.cln);
837    sec += (fat_fd->dir_pos.sname.ofs >> fs_info->vol.sec_log2);
838    byte = (fat_fd->dir_pos.sname.ofs & (fs_info->vol.bps - 1));
[f36a7bfc]839
[267de79a]840    if (fat_fd->fat_file_type == FAT_DIRECTORY) {
841      le_new_length = CT_LE_L(0);
842    } else {
843      le_new_length = CT_LE_L(fat_fd->fat_file_size);
844    }
845
[a7eaaae8]846    ret = fat_sector_write(fs_info, sec, byte + MSDOS_FILE_SIZE_OFFSET, 4,
[f36a7bfc]847                           (char *)(&le_new_length));
848    if ( ret < 0 )
849        return -1;
850
851    return RC_OK;
852}
853
854/*
855 * We should not check whether this routine is called for root dir - it
856 * never can happend
857 */
858
859/* msdos_set_first_char4file_name --
[a5305f6b]860 *     Write first character of the name of the file to the disk (to
861 *     corresponded 32bytes slot)
[f36a7bfc]862 *
863 * PARAMETERS:
[a5305f6b]864 *     mt_entry - mount table entry
[f36a7bfc]865 *     cl       - number of cluster
866 *     ofs      - offset inside cluster
867 *     fchar    - character to set up
868 *
869 * RETURNS:
870 *     RC_OK on success, or -1 if error occured (errno set apropriately)
871 *
872 */
[a5305f6b]873int
[f36a7bfc]874msdos_set_first_char4file_name(
[a5305f6b]875    rtems_filesystem_mount_table_entry_t *mt_entry,
[07d6fd5]876    fat_dir_pos_t                        *dir_pos,
[f36a7bfc]877    unsigned char                         fchar
878    )
879{
[07d6fd5]880    ssize_t          ret;
[f36a7bfc]881    msdos_fs_info_t *fs_info = mt_entry->fs_info;
[07d6fd5]882    uint32_t         dir_block_size;
883    fat_pos_t        start = dir_pos->lname;
884    fat_pos_t        end = dir_pos->sname;
[f36a7bfc]885
[07d6fd5]886    if ((end.cln == fs_info->fat.vol.rdir_cl) &&
887        (fs_info->fat.vol.type & (FAT_FAT12 | FAT_FAT16)))
888      dir_block_size = fs_info->fat.vol.rdir_size;
889    else
890      dir_block_size = fs_info->fat.vol.bpc;
[0a7278e]891
[07d6fd5]892    if (dir_pos->lname.cln == FAT_FILE_SHORT_NAME)
893      start = dir_pos->sname;
[f36a7bfc]894
[07d6fd5]895    /*
896     * We handle the changes directly due the way the short file
897     * name code was written rather than use the fat_file_write
898     * interface.
899     */
900    while (true)
901    {
[c65afce4]902      uint32_t sec = (fat_cluster_num_to_sector_num(&fs_info->fat, start.cln) +
[07d6fd5]903                      (start.ofs >> fs_info->fat.vol.sec_log2));
[721fe34]904      uint32_t byte = (start.ofs & (fs_info->fat.vol.bps - 1));
[07d6fd5]905
[42a22f08]906      ret = fat_sector_write(&fs_info->fat, sec, byte + MSDOS_FILE_NAME_OFFSET,
[c65afce4]907                             1, &fchar);
[07d6fd5]908      if (ret < 0)
[f36a7bfc]909        return -1;
910
[07d6fd5]911      if ((start.cln == end.cln) && (start.ofs == end.ofs))
912        break;
913
914      start.ofs += MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE;
915      if (start.ofs >= dir_block_size)
916      {
917        int rc;
918        if ((end.cln == fs_info->fat.vol.rdir_cl) &&
919            (fs_info->fat.vol.type & (FAT_FAT12 | FAT_FAT16)))
920          break;
[c65afce4]921        rc = fat_get_fat_cluster(&fs_info->fat, start.cln, &start.cln);
[07d6fd5]922        if ( rc != RC_OK )
923          return rc;
924        start.ofs = 0;
925      }
926    }
927
[f36a7bfc]928    return  RC_OK;
[a5305f6b]929}
[f36a7bfc]930
931/* msdos_dir_is_empty --
[a5305f6b]932 *     Check whether directory which correspondes to the fat-file descriptor is
933 *     empty.
[f36a7bfc]934 *
935 * PARAMETERS:
[a5305f6b]936 *     mt_entry - mount table entry
937 *     fat_fd   - fat-file descriptor
938 *     ret_val  - placeholder for result
[f36a7bfc]939 *
940 * RETURNS:
941 *     RC_OK on success, or -1 if error occured
942 *
943 */
944int
945msdos_dir_is_empty(
946    rtems_filesystem_mount_table_entry_t *mt_entry,
[a5305f6b]947    fat_file_fd_t                        *fat_fd,
[0a896eb]948    bool                                 *ret_val
[f36a7bfc]949    )
950{
951    ssize_t          ret = 0;
952    msdos_fs_info_t *fs_info = mt_entry->fs_info;
[f91bbe64]953    uint32_t         j = 0, i = 0;
[a5305f6b]954
[f36a7bfc]955    /* dir is not empty */
[0a896eb]956    *ret_val = false;
[f36a7bfc]957
[c65afce4]958    while ((ret = fat_file_read(&fs_info->fat, fat_fd, j * fs_info->fat.vol.bps,
[a5305f6b]959                                  fs_info->fat.vol.bps,
[f36a7bfc]960                                  fs_info->cl_buf)) != FAT_EOF)
961    {
962        if (ret < MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE)
963            return -1;
964
965        assert(ret == fs_info->fat.vol.bps);
[a5305f6b]966
[b17ff491]967        /* have to look at the DIR_NAME as "raw" 8-bit data */
[a5305f6b]968        for (i = 0;
969             i < fs_info->fat.vol.bps;
[f36a7bfc]970             i += MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE)
971        {
[07d6fd5]972            char* entry = (char*) fs_info->cl_buf + i;
973
974            /*
975             * If the entry is empty, a long file name entry, or '.' or '..'
976             * then consider it as empty.
977             *
978             * Just ignore long file name entries. They must have a short entry to
979             * be valid.
980             */
981            if (((*MSDOS_DIR_ENTRY_TYPE(entry)) ==
[a5305f6b]982                 MSDOS_THIS_DIR_ENTRY_EMPTY) ||
[07d6fd5]983                ((*MSDOS_DIR_ATTR(entry) & MSDOS_ATTR_LFN_MASK) ==
984                 MSDOS_ATTR_LFN) ||
985                (strncmp(MSDOS_DIR_NAME((entry)), MSDOS_DOT_NAME,
[f36a7bfc]986                         MSDOS_SHORT_NAME_LEN) == 0) ||
[07d6fd5]987                (strncmp(MSDOS_DIR_NAME((entry)),
[a5305f6b]988                         MSDOS_DOTDOT_NAME,
[f36a7bfc]989                         MSDOS_SHORT_NAME_LEN) == 0))
990                continue;
991
[07d6fd5]992            /*
993             * Nothing more to look at.
994             */
995            if ((*MSDOS_DIR_NAME(entry)) ==
[f36a7bfc]996                MSDOS_THIS_DIR_ENTRY_AND_REST_EMPTY)
997            {
[0a896eb]998                *ret_val = true;
[f36a7bfc]999                return RC_OK;
1000            }
[07d6fd5]1001
1002            /*
1003             * Short file name entries mean not empty.
1004             */
[f36a7bfc]1005            return RC_OK;
[a5305f6b]1006        }
1007        j++;
[f36a7bfc]1008    }
[0a896eb]1009    *ret_val = true;
[f36a7bfc]1010    return RC_OK;
1011}
1012
[d2e0bb3]1013#define MSDOS_FIND_PRINT 0
1014static int
1015msdos_on_entry_found (
1016    msdos_fs_info_t                      *fs_info,
1017    fat_file_fd_t                        *fat_fd,
1018    const uint32_t                        bts2rd,
1019    char                                 *name_dir_entry,
1020    char                                 *entry,
1021    fat_dir_pos_t                        *dir_pos,
[b1daf0f]1022    uint32_t                              dir_offset,
[d2e0bb3]1023    const uint32_t                        dir_entry,
1024    const fat_pos_t                      *lfn_start
1025)
1026{
1027    int rc = RC_OK;
1028#if MSDOS_FIND_PRINT
1029    printf ("MSFS:[9.3] SNF found\n");
1030#endif
1031    /*
1032     * We get the entry we looked for - fill the position
1033     * structure and the 32 bytes of the short entry
1034     */
1035    rc = fat_file_ioctl(&fs_info->fat,
1036                        fat_fd,
1037                        F_CLU_NUM,
[b1daf0f]1038                        dir_offset * bts2rd,
[d2e0bb3]1039                        &dir_pos->sname.cln);
1040    if (rc == RC_OK) {
1041        dir_pos->sname.ofs = dir_entry;
1042
1043        if (lfn_start->cln != FAT_FILE_SHORT_NAME)
1044        {
1045            rc = fat_file_ioctl (&fs_info->fat,
1046                                 fat_fd,
1047                                 F_CLU_NUM,
1048                                 lfn_start->cln * bts2rd,
1049                                 &lfn_start->cln);
1050        }
1051        if ( rc == RC_OK ) {
1052            dir_pos->lname.cln = lfn_start->cln;
1053            dir_pos->lname.ofs = lfn_start->ofs;
1054
1055            memcpy(name_dir_entry, entry,
1056                   MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE);
1057        }
1058    }
1059
1060    return rc;
1061}
1062
1063ssize_t
1064msdos_get_utf16_string_from_long_entry (
1065    const char                 *entry,
1066    uint16_t                   *entry_string_buf,
1067    const size_t                buf_size,
1068    bool                        is_first_entry
1069)
1070{
1071    ssize_t chars_in_entry;
1072
[83a4cbb]1073    if (buf_size >= MSDOS_LFN_ENTRY_SIZE) {
[d2e0bb3]1074        memcpy (&entry_string_buf[0],  &entry[1],  10 );
1075        memcpy (&entry_string_buf[5],  &entry[14], 12 );
1076        memcpy (&entry_string_buf[11], &entry[28],  4 );
1077
1078        if (is_first_entry) {
1079            for (chars_in_entry = 0;
[92422be]1080                 (   chars_in_entry < MSDOS_LFN_LEN_PER_ENTRY
1081                  && entry_string_buf[chars_in_entry] != 0x0000);
[d2e0bb3]1082                  ++chars_in_entry) {
1083                ;
1084            }
1085        }
1086        else
1087            chars_in_entry = MSDOS_LFN_LEN_PER_ENTRY;
1088
1089        return chars_in_entry * MSDOS_NAME_LFN_BYTES_PER_CHAR;
1090    }
1091    else
1092        return ENOMEM;
1093}
1094
1095/*  msdos_format_dirent_with_dot --
1096 *      This routine convert a (short) MSDOS filename as present on disk
1097 *      (fixed 8+3 characters, filled with blanks, without separator dot)
1098 *      to a "normal" format, with between 0 and 8 name chars,
1099 *      a separating dot and up to 3 extension characters
1100 *   Rules to work:
1101 *      - copy any (0-8) "name" part characters that are non-blank
1102 *      - if an extension exists, append a dot
1103 *      - copy any (0-3) non-blank extension characters
1104 *      - append a '\0' (dont count it for the rturn code
[f36a7bfc]1105 *
1106 * PARAMETERS:
[d2e0bb3]1107 *     dst: pointer to destination char array (must be big enough)
1108 *     src: pointer to source characters
[f36a7bfc]1109 *
1110 *
[d2e0bb3]1111 * RETURNS:
1112 *     the number of bytes (without trailing '\0'(written to destination
[f36a7bfc]1113 */
[d2e0bb3]1114ssize_t
1115msdos_format_dirent_with_dot(char *dst,const char *src)
[f36a7bfc]1116{
[d2e0bb3]1117  ssize_t len;
1118  int i;
1119  const char *src_tmp;
1120
1121  /*
1122   * find last non-blank character of base name
1123   */
1124  for (i = MSDOS_SHORT_BASE_LEN, src_tmp = src + MSDOS_SHORT_BASE_LEN - 1;
1125       i > 0 && *src_tmp == ' ';
1126       --i,--src_tmp)
1127    {};
1128  /*
1129   * copy base name to destination
1130   */
1131  src_tmp = src;
1132  len = i;
1133  while (i-- > 0) {
1134    *dst++ = tolower((unsigned char)(*src_tmp++));
1135  }
1136  /*
1137   * find last non-blank character of extension
1138   */
1139  for (i = MSDOS_SHORT_EXT_LEN,
1140        src_tmp = src + MSDOS_SHORT_BASE_LEN+MSDOS_SHORT_EXT_LEN-1;
1141       i > 0 && *src_tmp == ' ';
1142       --i, --src_tmp)
1143    {};
1144  /*
1145   * extension is not empty
1146   */
1147  if (i > 0) {
1148    *dst++ = '.'; /* append dot */
1149    ++len;        /* dot */
1150    src_tmp = src + MSDOS_SHORT_BASE_LEN;
1151    while (i-- > 0) {
1152      *dst++ = tolower((unsigned char)(*src_tmp++));
1153      ++len;
1154    }
1155  }
1156  *dst = '\0'; /* terminate string */
[07d6fd5]1157
[d2e0bb3]1158  return len;
1159}
[0a7278e]1160
[d2e0bb3]1161static ssize_t
1162msdos_long_entry_to_utf8_name (
1163    rtems_dosfs_convert_control *converter,
1164    const char                  *entry,
1165    const bool                   is_first_entry,
1166    uint8_t                     *entry_utf8_buf,
1167    const size_t                 buf_size)
1168{
1169    ssize_t      retval         = 0;
1170    int          eno            = 0;
1171    size_t       bytes_in_utf8  = buf_size;
1172    size_t       bytes_in_buf;
1173    uint16_t     entry_string[MSDOS_LFN_LEN_PER_ENTRY];
1174
1175    retval = msdos_get_utf16_string_from_long_entry (
1176        entry,
1177        entry_string,
1178        sizeof (entry_string),
1179        is_first_entry
1180    );
1181
1182    if (retval >= 0) {
1183        bytes_in_buf = retval;
1184        eno = (*converter->handler->utf16_to_utf8) (
1185            converter,
1186            &entry_string[0],
1187            bytes_in_buf,
1188            &entry_utf8_buf[0],
1189            &bytes_in_utf8);
1190        if ( eno == 0 ) {
1191            retval = bytes_in_utf8;
1192        }
1193    }
[0a7278e]1194
[d2e0bb3]1195    if (eno != 0) {
1196        retval = -1;
1197        errno  = eno;
1198    }
[0a7278e]1199
[d2e0bb3]1200    return retval;
1201}
[a5305f6b]1202
[d2e0bb3]1203static ssize_t msdos_short_entry_to_utf8_name (
1204  rtems_dosfs_convert_control     *converter,
1205  const char                      *entry,
1206  uint8_t                         *buf,
1207  const size_t                     buf_size)
1208{
1209  char         char_buf[MSDOS_NAME_MAX_WITH_DOT];
1210  int          eno             = 0;
1211  size_t       bytes_converted = buf_size;
1212  ssize_t      bytes_written   = msdos_format_dirent_with_dot(char_buf, entry);
1213
1214  if (bytes_written > 0) {
1215    if (char_buf[0] == 0x05)
1216      char_buf[0] = 0xE5;
1217
1218    eno = (*converter->handler->codepage_to_utf8) (
1219        converter,
1220        char_buf,
1221        bytes_written,
1222        buf,
1223        &bytes_converted);
1224    if (eno == 0)
1225        bytes_written = bytes_converted;
1226  } else {
1227      eno = EINVAL;
1228  }
1229
1230  if (eno != 0) {
1231      bytes_written = -1;
1232      errno         = eno;
1233  }
1234
1235  return bytes_written;
1236}
[07d6fd5]1237
[d2e0bb3]1238static ssize_t
1239msdos_compare_entry_against_filename (
1240    rtems_dosfs_convert_control *converter,
1241    const uint8_t               *entry,
1242    const size_t                 entry_size,
1243    const uint8_t               *filename,
[a2c204eb]1244    const size_t                 name_len_remaining,
[d2e0bb3]1245    bool                        *is_matching)
1246{
[a2c204eb]1247  ssize_t      size_remaining = name_len_remaining;
[d2e0bb3]1248  int          eno            = 0;
[83a4cbb]1249  uint8_t      entry_normalized[MSDOS_LFN_ENTRY_SIZE_UTF8];
[d2e0bb3]1250  size_t       bytes_in_entry_normalized = sizeof ( entry_normalized );
1251
1252  eno = (*converter->handler->utf8_normalize_and_fold) (
1253      converter,
1254      &entry[0],
1255      entry_size,
1256      &entry_normalized[0],
1257      &bytes_in_entry_normalized);
1258  if (eno == 0) {
1259#if MSDOS_FIND_PRINT > 1
1260        printf ( "MSFS:[6] entry_normalized:%s"
1261                 "name:%s\n",
1262                 entry,
1263                 filename );
[07d6fd5]1264#endif
[d2e0bb3]1265        if (bytes_in_entry_normalized <= size_remaining) {
1266            size_remaining = size_remaining - bytes_in_entry_normalized;
1267            if (0 == memcmp ( &entry_normalized[0],
1268                              &filename[size_remaining],
1269                              bytes_in_entry_normalized)) {
1270                *is_matching = true;
1271            } else {
1272                *is_matching   = false;
[a2c204eb]1273                size_remaining = name_len_remaining;
[d2e0bb3]1274            }
1275
1276        }
1277        else {
1278          *is_matching = false;
1279        }
1280    }
1281
1282    if (eno != 0) {
1283      size_remaining = -1;
1284      errno          = eno;
1285    }
1286
1287    return size_remaining;
1288}
1289
[a2c204eb]1290static void
1291msdos_prepare_for_next_entry(
1292    fat_pos_t *lfn_start,
1293    bool      *entry_matched,
1294    ssize_t   *name_len_remaining,
1295    size_t     name_len_for_compare)
1296{
1297    lfn_start->cln = FAT_FILE_SHORT_NAME;
1298    *entry_matched = false;
1299    *name_len_remaining = name_len_for_compare;
1300}
1301
[d2e0bb3]1302static int
1303msdos_find_file_in_directory (
1304    const uint8_t                        *filename_converted,
1305    const size_t                          name_len_for_compare,
1306    const size_t                          name_len_for_save,
1307    const msdos_name_type_t               name_type,
1308    msdos_fs_info_t                      *fs_info,
1309    fat_file_fd_t                        *fat_fd,
1310    const uint32_t                        bts2rd,
1311    const bool                            create_node,
[6c988987]1312    const unsigned int                    lfn_entries,
[d2e0bb3]1313    char                                 *name_dir_entry,
1314    fat_dir_pos_t                        *dir_pos,
[b1daf0f]1315    uint32_t                             *empty_file_offset,
1316    uint32_t                             *empty_entry_count)
[d2e0bb3]1317{
1318    int               rc                = RC_OK;
1319    ssize_t           bytes_read;
1320    uint32_t          dir_entry;
1321    fat_pos_t         lfn_start;
1322    uint8_t           lfn_checksum      = 0;
[a2c204eb]1323    bool              entry_matched;
[d2e0bb3]1324    bool              empty_space_found = false;
1325    uint32_t          entries_per_block = bts2rd / MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE;
1326    int               lfn_entry         = 0;
[83a4cbb]1327    uint8_t           entry_utf8_normalized[MSDOS_LFN_ENTRY_SIZE_UTF8];
[d2e0bb3]1328    size_t            bytes_in_entry;
1329    bool              filename_matched  = false;
[a2c204eb]1330    ssize_t           name_len_remaining;
[d2e0bb3]1331    rtems_dosfs_convert_control *converter = fs_info->converter;
[b1daf0f]1332    uint32_t          dir_offset = 0;
[d2e0bb3]1333
[07d6fd5]1334    /*
1335     * Scan the directory seeing if the file is present. While
1336     * doing this see if a suitable location can be found to
1337     * create the entry if the name is not found.
1338     */
[d2e0bb3]1339
[a2c204eb]1340    msdos_prepare_for_next_entry(&lfn_start, &entry_matched,
1341                                 &name_len_remaining,
1342                                 name_len_for_compare);
[d2e0bb3]1343
[b1daf0f]1344    while (   (bytes_read = fat_file_read (&fs_info->fat, fat_fd, (dir_offset * bts2rd),
[d2e0bb3]1345                                             bts2rd, fs_info->cl_buf)) != FAT_EOF
1346           && rc == RC_OK)
[f36a7bfc]1347    {
[07d6fd5]1348        bool remainder_empty = false;
1349#if MSDOS_FIND_PRINT
[b1daf0f]1350        printf ("MSFS:[2] dir_offset:%li\n", dir_offset);
[07d6fd5]1351#endif
[0a7278e]1352
[d2e0bb3]1353        if (bytes_read < MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE)
[3899a537]1354            rtems_set_errno_and_return_minus_one(EIO);
[a5305f6b]1355
[d2e0bb3]1356        assert(bytes_read == bts2rd);
[f36a7bfc]1357
[b17ff491]1358        /* have to look at the DIR_NAME as "raw" 8-bit data */
[07d6fd5]1359        for (dir_entry = 0;
[d2e0bb3]1360             dir_entry < bts2rd && rc == RC_OK && (! filename_matched);
[07d6fd5]1361             dir_entry += MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE)
[f36a7bfc]1362        {
[07d6fd5]1363            char* entry = (char*) fs_info->cl_buf + dir_entry;
1364
1365            /*
1366             * See if the entry is empty or the remainder of the directory is
[d2e0bb3]1367             * empty ? Localize to make the code read better.
[07d6fd5]1368             */
1369            bool entry_empty = (*MSDOS_DIR_ENTRY_TYPE(entry) ==
1370                                MSDOS_THIS_DIR_ENTRY_EMPTY);
1371            remainder_empty = (*MSDOS_DIR_ENTRY_TYPE(entry) ==
1372                               MSDOS_THIS_DIR_ENTRY_AND_REST_EMPTY);
1373#if MSDOS_FIND_PRINT
1374            printf ("MSFS:[3] re:%i ee:%i do:%li de:%li(%ld)\n",
[b1daf0f]1375                    remainder_empty, entry_empty, dir_offset,
[07d6fd5]1376                    dir_entry, (dir_entry / MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE));
1377#endif
1378            /*
1379             * Remember where the we are, ie the start, so we can come back
1380             * to here and write the long file name if this is the start of
[b1daf0f]1381             * a series of empty entries. If empty_entry_count is 0 then
[07d6fd5]1382             * we are currently not inside an empty series of entries. It
1383             * is a count of empty entries.
1384             */
[b1daf0f]1385            if (*empty_entry_count == 0)
[f36a7bfc]1386            {
[b1daf0f]1387                *empty_file_offset = dir_offset * bts2rd + dir_entry;
[07d6fd5]1388            }
[f36a7bfc]1389
[07d6fd5]1390            if (remainder_empty)
1391            {
1392#if MSDOS_FIND_PRINT
1393                printf ("MSFS:[3.1] cn:%i esf:%i\n", create_node, empty_space_found);
1394#endif
[a5305f6b]1395                /*
[07d6fd5]1396                 * If just looking and there is no more entries in the
[f36a7bfc]1397                 * directory - return name-not-found
1398                 */
[07d6fd5]1399                if (!create_node)
[d2e0bb3]1400                    rc = MSDOS_NAME_NOT_FOUND_ERR;
[07d6fd5]1401
1402                /*
1403                 * Lets go and write the directory entries. If we have not found
1404                 * any available space add the remaining number of entries to any that
1405                 * we may have already found that are just before this entry. If more
1406                 * are needed FAT_EOF is returned by the read and we extend the file.
1407                 */
[d2e0bb3]1408                if (   !empty_space_found
1409                    && rc == RC_OK )
[07d6fd5]1410                {
[b1daf0f]1411                    *empty_entry_count +=
[07d6fd5]1412                    entries_per_block - (dir_entry / MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE);
[d2e0bb3]1413                    empty_space_found = true;
[07d6fd5]1414#if MSDOS_FIND_PRINT
[b1daf0f]1415                    printf ( "MSFS:[3.2] esf:%i esc%"PRIu32"\n", empty_space_found, *empty_entry_count );
[07d6fd5]1416#endif
1417                }
1418                break;
1419            }
1420            else if (entry_empty)
1421            {
1422                if (create_node)
1423                {
[d2e0bb3]1424                    /*
1425                     * Remainder is not empty so is this entry empty ?
1426                     */
[b1daf0f]1427                    (*empty_entry_count)++;
[0a7278e]1428
[6c988987]1429                    if (*empty_entry_count == (lfn_entries + 1))
[d2e0bb3]1430                        empty_space_found = true;
[07d6fd5]1431                }
1432#if MSDOS_FIND_PRINT
1433                printf ("MSFS:[4.1] esc:%li esf:%i\n",
[b1daf0f]1434                        *empty_entry_count, empty_space_found);
[07d6fd5]1435#endif
[a2c204eb]1436                msdos_prepare_for_next_entry(&lfn_start, &entry_matched,
1437                                             &name_len_remaining,
1438                                             name_len_for_compare);
[a5305f6b]1439            }
[f36a7bfc]1440            else
1441            {
[07d6fd5]1442                /*
1443                 * A valid entry so handle it.
1444                 *
1445                 * If empty space has not been found we need to start the
1446                 * count again.
1447                 */
1448                if (create_node && !empty_space_found)
1449                {
[b1daf0f]1450                    *empty_file_offset = 0;
1451                    *empty_entry_count = 0;
[07d6fd5]1452                }
1453
1454                /*
1455                 * Check the attribute to see if the entry is for a long
1456                 * file name.
1457                 */
1458                if ((*MSDOS_DIR_ATTR(entry) & MSDOS_ATTR_LFN_MASK) ==
1459                    MSDOS_ATTR_LFN)
[f36a7bfc]1460                {
[d50ab079]1461                    bool is_first_lfn_entry =
1462                        (lfn_start.cln == FAT_FILE_SHORT_NAME);
1463
[d2e0bb3]1464/*                    int   o;*/
[07d6fd5]1465#if MSDOS_FIND_PRINT
1466                    printf ("MSFS:[4.2] lfn:%c entry:%i checksum:%i\n",
[8f77f8f]1467                            lfn_start.cln == FAT_FILE_SHORT_NAME ? 'f' : 't',
[07d6fd5]1468                            *MSDOS_DIR_ENTRY_TYPE(entry) & MSDOS_LAST_LONG_ENTRY_MASK,
1469                            *MSDOS_DIR_LFN_CHECKSUM(entry));
1470#endif
1471                    /*
1472                     * If we are not already processing a LFN see if this is
1473                     * the first entry of a LFN ?
1474                     */
[d50ab079]1475                    if (is_first_lfn_entry)
[f36a7bfc]1476                    {
[d2e0bb3]1477                        entry_matched = false;
[07d6fd5]1478
1479                        /*
1480                         * The first entry must have the last long entry
1481                         * flag set.
1482                         */
1483                        if ((*MSDOS_DIR_ENTRY_TYPE(entry) &
1484                             MSDOS_LAST_LONG_ENTRY) == 0)
1485                            continue;
1486
[b1daf0f]1487                        lfn_start.cln = dir_offset;
[07d6fd5]1488                        lfn_start.ofs = dir_entry;
[d50ab079]1489                        lfn_entry = (*MSDOS_DIR_ENTRY_TYPE(entry)
1490                            & MSDOS_LAST_LONG_ENTRY_MASK);
[07d6fd5]1491                        lfn_checksum = *MSDOS_DIR_LFN_CHECKSUM(entry);
[0a7278e]1492
[07d6fd5]1493#if MSDOS_FIND_PRINT
1494                        printf ("MSFS:[4.3] lfn_checksum:%i\n",
[d2e0bb3]1495                                lfn_checksum);
[07d6fd5]1496#endif
1497                    }
1498
1499                    /*
1500                     * If the entry number or the check sum do not match
1501                     * forget this series of long directory entries. These
1502                     * could be orphaned entries depending on the history
1503                     * of the disk.
1504                     */
1505                    if ((lfn_entry != (*MSDOS_DIR_ENTRY_TYPE(entry) &
1506                                       MSDOS_LAST_LONG_ENTRY_MASK)) ||
1507                        (lfn_checksum != *MSDOS_DIR_LFN_CHECKSUM(entry)))
1508                    {
1509#if MSDOS_FIND_PRINT
1510                        printf ("MSFS:[4.4] no match\n");
1511#endif
[a2c204eb]1512                        msdos_prepare_for_next_entry(&lfn_start,
1513                                                     &entry_matched,
1514                                                     &name_len_remaining,
1515                                                     name_len_for_compare);
[07d6fd5]1516                        continue;
1517                    }
1518#if MSDOS_FIND_PRINT
1519                    printf ("MSFS:[5] lfne:%i\n", lfn_entry);
1520#endif
[d2e0bb3]1521                    lfn_entry--;
[0a7278e]1522
[d2e0bb3]1523                    bytes_in_entry = msdos_long_entry_to_utf8_name (
1524                        converter,
1525                        entry,
[d50ab079]1526                        is_first_lfn_entry,
[d2e0bb3]1527                        &entry_utf8_normalized[0],
1528                        sizeof (entry_utf8_normalized));
1529                    if (bytes_in_entry > 0) {
[a2c204eb]1530                        name_len_remaining = msdos_compare_entry_against_filename (
[d2e0bb3]1531                            converter,
1532                            &entry_utf8_normalized[0],
1533                            bytes_in_entry,
1534                            &filename_converted[0],
[a2c204eb]1535                            name_len_remaining,
[d2e0bb3]1536                            &entry_matched);
1537
[a2c204eb]1538                        if (name_len_remaining < 0 || !entry_matched) {
1539                            msdos_prepare_for_next_entry(&lfn_start,
1540                                                         &entry_matched,
1541                                                         &name_len_remaining,
1542                                                         name_len_for_compare);
[07d6fd5]1543                        }
[d2e0bb3]1544                    } else {
[a2c204eb]1545                        msdos_prepare_for_next_entry(&lfn_start,
1546                                                     &entry_matched,
1547                                                     &name_len_remaining,
1548                                                     name_len_for_compare);
[07d6fd5]1549                    }
1550                }
1551                else
1552                {
[8f77f8f]1553#if MSDOS_FIND_PRINT
[d2e0bb3]1554                    printf ("MSFS:[9.1] SFN entry, entry_matched:%i\n", entry_matched);
[8f77f8f]1555#endif
[07d6fd5]1556                    /*
1557                     * SFN entry found.
1558                     *
1559                     * If a LFN has been found and it matched check the
1560                     * entries have all been found and the checksum is
1561                     * correct. If this is the case return the short file
1562                     * name entry.
1563                     */
[d2e0bb3]1564                    if (entry_matched)
[07d6fd5]1565                    {
[3efe749]1566                        if (lfn_entry ||
1567                            lfn_checksum != msdos_lfn_checksum(entry))
[d2e0bb3]1568                            entry_matched = false;
[a2c204eb]1569                        else if (name_len_remaining == 0) {
[d2e0bb3]1570                            filename_matched = true;
1571                            rc = msdos_on_entry_found (
1572                                fs_info,
1573                                fat_fd,
1574                                bts2rd,
1575                                name_dir_entry,
1576                                entry,
1577                                dir_pos,
1578                                dir_offset,
1579                                dir_entry,
1580                                &lfn_start
1581                            );
1582                        }
[07d6fd5]1583
[8f77f8f]1584#if MSDOS_FIND_PRINT
[d2e0bb3]1585                        printf ("MSFS:[9.2] checksum, entry_matched:%i, lfn_entry:%i, lfn_checksum:%02x/%02x\n",
[3efe749]1586                                entry_matched, lfn_entry, lfn_checksum, msdos_lfn_checksum(entry));
[8f77f8f]1587#endif
[d2e0bb3]1588                    } else {
1589                        bytes_in_entry = MSDOS_SHORT_NAME_LEN + 1;
1590                        bytes_in_entry = msdos_short_entry_to_utf8_name (
1591                            converter,
1592                            MSDOS_DIR_NAME (entry),
1593                            &entry_utf8_normalized[0],
1594                            bytes_in_entry);
1595                        if (bytes_in_entry > 0) {
[a2c204eb]1596                            name_len_remaining = msdos_compare_entry_against_filename (
[d2e0bb3]1597                                converter,
1598                                &entry_utf8_normalized[0],
1599                                bytes_in_entry,
1600                                &filename_converted[0],
1601                                name_len_for_compare,
1602                                &entry_matched);
[a2c204eb]1603                            if (entry_matched && name_len_remaining == 0) {
[d2e0bb3]1604                                filename_matched = true;
1605                                rc = msdos_on_entry_found (
1606                                    fs_info,
1607                                    fat_fd,
1608                                    bts2rd,
1609                                    name_dir_entry,
1610                                    entry,
1611                                    dir_pos,
1612                                    dir_offset,
1613                                    dir_entry,
1614                                    &lfn_start
1615                                );
1616                            }
[a2c204eb]1617                            if (rc == RC_OK && !filename_matched) {
1618                                msdos_prepare_for_next_entry(&lfn_start,
1619                                                             &entry_matched,
1620                                                             &name_len_remaining,
1621                                                             name_len_for_compare);
[d2e0bb3]1622                            }
1623                        } else {
[a2c204eb]1624                          msdos_prepare_for_next_entry(&lfn_start,
1625                                                       &entry_matched,
1626                                                       &name_len_remaining,
1627                                                       name_len_for_compare);
[07d6fd5]1628                        }
[f36a7bfc]1629                    }
[a5305f6b]1630                }
1631            }
1632        }
[07d6fd5]1633
[d2e0bb3]1634        if (filename_matched || remainder_empty)
[07d6fd5]1635            break;
1636
[b1daf0f]1637        dir_offset++;
[a5305f6b]1638    }
[d2e0bb3]1639    if ( ! filename_matched ) {
1640        /*
1641         * If we are not to create the entry return a not found error.
1642         */
1643        if (!create_node)
1644            rc = MSDOS_NAME_NOT_FOUND_ERR;
[07d6fd5]1645
1646#if MSDOS_FIND_PRINT
[b1daf0f]1647        printf ( "MSFS:[8.1] WRITE do:%"PRIu32" esc:%"PRIu32" efo:%"PRIu32"\n",
1648                 dir_offset, *empty_entry_count, *empty_file_offset );
[07d6fd5]1649#endif
[d2e0bb3]1650    }
1651
1652    return rc;
1653}
1654
[b1daf0f]1655static int
1656msdos_get_pos(
1657    msdos_fs_info_t *fs_info,
1658    fat_file_fd_t   *fat_fd,
1659    uint32_t         bts2rd,
1660    uint32_t         file_offset,
1661    fat_pos_t       *pos
1662)
1663{
1664    pos->ofs = file_offset & (bts2rd - 1);
1665    return fat_file_ioctl(&fs_info->fat, fat_fd, F_CLU_NUM,
1666                          file_offset, &pos->cln);
1667}
1668
[d2e0bb3]1669static int
1670msdos_add_file (
1671    const char                           *name_converted,
1672    const msdos_name_type_t               name_type,
1673    msdos_fs_info_t                      *fs_info,
1674    fat_file_fd_t                        *fat_fd,
1675    const uint32_t                        bts2rd,
[6c988987]1676    const unsigned int                    lfn_entries,
[d2e0bb3]1677    const char                           *name_dir_entry,
1678    fat_dir_pos_t                        *dir_pos,
[b1daf0f]1679    uint32_t                              empty_file_offset,
1680    const uint32_t                        empty_entry_count
[d2e0bb3]1681)
1682{
[b1daf0f]1683    int              ret;
1684    ssize_t          bytes_written;
1685    uint8_t          lfn_checksum;
1686    int              lfn_entry;
1687    uint8_t         *entry;
1688    uint32_t         short_file_offset;
1689    uint32_t         length;
[07d6fd5]1690
1691    /*
[b1daf0f]1692     * If there is not enough space available then extend the file.
[07d6fd5]1693     */
[6c988987]1694    if (empty_entry_count < lfn_entries + 1)
[07d6fd5]1695    {
[b1daf0f]1696        uint32_t unused;
1697
1698        empty_file_offset = fat_fd->fat_file_size -
1699            empty_entry_count * MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE;
1700
1701        ret = fat_file_extend(&fs_info->fat,
1702                              fat_fd,
1703                              true,
1704                              fat_fd->fat_file_size + fs_info->fat.vol.bpc,
1705                              &unused);
1706        if (ret != RC_OK)
1707            return ret;
[8f77f8f]1708    }
[07d6fd5]1709
[b1daf0f]1710    if (name_type == MSDOS_NAME_LONG)
[8f77f8f]1711    {
[b1daf0f]1712        uint32_t slot;
1713
1714        /*
1715         * If a long file name calculate the checksum of the short file name
1716         * data to place in each long file name entry. First set the short
1717         * file name to the slot of the SFN entry. This will mean no clashes
1718         * in this directory.
1719         */
1720        slot = (empty_file_offset /
[6c988987]1721            MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE) + lfn_entries + 1;
[b1daf0f]1722        msdos_short_name_hex(MSDOS_DIR_NAME(name_dir_entry), slot);
1723
[3efe749]1724        lfn_checksum = msdos_lfn_checksum(name_dir_entry);
[07d6fd5]1725
[6c988987]1726        short_file_offset = empty_file_offset + lfn_entries
[b1daf0f]1727            * MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE;
1728
1729        /* Get position of first long file name entry */
1730        ret = msdos_get_pos(fs_info, fat_fd, bts2rd, empty_file_offset,
1731                            &dir_pos->lname);
1732        if (ret != RC_OK)
1733            return ret;
1734    } else {
1735        lfn_checksum = 0;
1736        short_file_offset = empty_file_offset;
1737        dir_pos->lname.cln = FAT_FILE_SHORT_NAME;
1738        dir_pos->lname.ofs = FAT_FILE_SHORT_NAME;
[07d6fd5]1739    }
1740
[b1daf0f]1741    /* Get position of short file name entry */
1742    ret = msdos_get_pos(fs_info, fat_fd, bts2rd, short_file_offset,
1743                        &dir_pos->sname);
[07d6fd5]1744
1745    /*
1746     * Handle the entry writes.
1747     */
[b1daf0f]1748    entry = fs_info->cl_buf;
[07d6fd5]1749
1750#if MSDOS_FIND_PRINT
[b1daf0f]1751    printf ("MSFS:[9] read_cluster:%d efo:%ld ese:%ld\n",
1752            read_cluster, empty_file_offset, empty_space_entry);
[07d6fd5]1753#endif
1754
[b1daf0f]1755    /* Long file name entries */
[6c988987]1756    for (lfn_entry = 0; lfn_entry < lfn_entries; ++lfn_entry) {
[b1daf0f]1757        uint8_t       *p;
1758        const uint8_t *n;
1759        int            i;
1760        uint8_t        fill = 0;
[0a7278e]1761
[b1daf0f]1762        /*
1763         * Clear the entry before loading the data.
1764         */
1765        memset (entry, 0, MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE);
[07d6fd5]1766
[b1daf0f]1767        *MSDOS_DIR_LFN_CHECKSUM(entry) = lfn_checksum;
[07d6fd5]1768
[b1daf0f]1769        p = entry + 1;
1770        n = (const uint8_t *) name_converted +
[6c988987]1771            (lfn_entries - lfn_entry - 1) * MSDOS_LFN_ENTRY_SIZE;
[07d6fd5]1772
1773#if MSDOS_FIND_PRINT
[b1daf0f]1774        printf ("MSFS:[11] ");
[07d6fd5]1775#endif
[b1daf0f]1776        for (i = 0; i < MSDOS_LFN_LEN_PER_ENTRY; ++i)
[07d6fd5]1777        {
[5f8ed0d]1778            if (*n != 0 || *(n + 1) != 0)
[07d6fd5]1779            {
[b1daf0f]1780                *p = *n;
[5f8ed0d]1781                *(p + 1) = *(n + 1);
1782                n += MSDOS_NAME_LFN_BYTES_PER_CHAR;
[07d6fd5]1783            }
[b1daf0f]1784            else
[07d6fd5]1785            {
[b1daf0f]1786                p [0] = fill;
1787                p [1] = fill;
1788                fill = 0xff;
[07d6fd5]1789            }
[d2e0bb3]1790#if MSDOS_FIND_PRINT
[b1daf0f]1791            printf ( "'%c''%c'", *p, *(p+1) );
[d2e0bb3]1792#endif
[0a7278e]1793
[b1daf0f]1794            switch (i)
1795            {
1796                case 4:
1797                    p += 5;
1798                    break;
1799                case 10:
1800                    p += 4;
1801                    break;
1802                default:
1803                    p += 2;
1804                    break;
[07d6fd5]1805            }
[b1daf0f]1806        }
[d2e0bb3]1807#if MSDOS_FIND_PRINT
[b1daf0f]1808        printf ( "\n" );
[d2e0bb3]1809#endif
[6c988987]1810        *MSDOS_DIR_ENTRY_TYPE(entry) = lfn_entries - lfn_entry;
[b1daf0f]1811        if (lfn_entry == 0)
1812            *MSDOS_DIR_ENTRY_TYPE(entry) |= MSDOS_LAST_LONG_ENTRY;
1813        *MSDOS_DIR_ATTR(entry) |= MSDOS_ATTR_LFN;
[07d6fd5]1814
[b1daf0f]1815        entry += MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE;
[07d6fd5]1816    }
[b1daf0f]1817
1818    /* Short file name entry */
1819    memcpy(entry, name_dir_entry, MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE);
1820
[6c988987]1821    length = (lfn_entries + 1) * MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE;
[b1daf0f]1822    bytes_written = fat_file_write(&fs_info->fat, fat_fd,
1823                                   empty_file_offset,
1824                                   length, fs_info->cl_buf);
1825    if (bytes_written == (ssize_t) length)
1826        return 0;
1827    else if (bytes_written == -1)
1828        return -1;
1829    else
1830        rtems_set_errno_and_return_minus_one(EIO);
[d2e0bb3]1831}
1832
1833int
1834msdos_find_name_in_fat_file (
1835    rtems_filesystem_mount_table_entry_t *mt_entry,
1836    fat_file_fd_t                        *fat_fd,
1837    bool                                  create_node,
1838    const uint8_t                        *name_utf8,
1839    int                                   name_utf8_len,
1840    msdos_name_type_t                     name_type,
1841    fat_dir_pos_t                        *dir_pos,
1842    char                                 *name_dir_entry)
1843{
1844    int                                retval                     = 0;
1845    msdos_fs_info_t                   *fs_info                    = mt_entry->fs_info;
1846    ssize_t                            name_len_for_save;
1847    ssize_t                            name_len_for_compare;
1848    uint32_t                           bts2rd                     = 0;
[b1daf0f]1849    uint32_t                           empty_file_offset          = 0;
1850    uint32_t                           empty_entry_count          = 0;
[6c988987]1851    unsigned int                       lfn_entries;
[d2e0bb3]1852    rtems_dosfs_convert_control       *converter = fs_info->converter;
1853    void                              *buffer = converter->buffer.data;
1854    size_t                             buffer_size = converter->buffer.size;
1855
1856    assert(name_utf8_len > 0);
1857
1858    fat_dir_pos_init(dir_pos);
1859
1860
[0a7278e]1861
[d2e0bb3]1862    if (FAT_FD_OF_ROOT_DIR(fat_fd) &&
1863        (fs_info->fat.vol.type & (FAT_FAT12 | FAT_FAT16)))
1864        bts2rd = fat_fd->fat_file_size;
1865    else
1866        bts2rd = fs_info->fat.vol.bpc;
1867
1868    switch ( name_type ) {
1869        case MSDOS_NAME_SHORT:
1870            name_len_for_compare = msdos_filename_utf8_to_short_name_for_compare (
1871                converter,
1872                name_utf8,
1873                name_utf8_len,
1874                buffer,
1875                MSDOS_SHORT_NAME_LEN);
1876            if (name_len_for_compare > 0) {
[6c988987]1877                lfn_entries = 0;
[d2e0bb3]1878            }
1879            else
1880                retval = -1;
1881        break;
1882        case MSDOS_NAME_LONG:
1883            name_len_for_save = msdos_filename_utf8_to_long_name_for_save (
1884                converter,
1885                name_utf8,
1886                name_utf8_len,
1887                buffer,
1888                buffer_size);
1889            if (name_len_for_save > 0) {
[6c988987]1890                lfn_entries = (name_len_for_save + MSDOS_LFN_ENTRY_SIZE - 1)
[83a4cbb]1891                    / MSDOS_LFN_ENTRY_SIZE;
[d2e0bb3]1892                name_len_for_compare = msdos_filename_utf8_to_long_name_for_compare (
1893                  converter,
1894                  name_utf8,
1895                  name_utf8_len,
1896                  buffer,
1897                  buffer_size);
1898                if (0 >= name_len_for_compare) {
1899                    retval = -1;
1900                }
1901            }
1902            else
1903                retval = -1;
1904        break;
1905        default:
1906            errno = EINVAL;
1907            retval = -1;
1908        break;
1909    }
1910    if (retval == RC_OK) {
1911      /* See if the file/directory does already exist */
1912      retval = msdos_find_file_in_directory (
1913          buffer,
1914          name_len_for_compare,
1915          name_len_for_save,
1916          name_type,
1917          fs_info,
1918          fat_fd,
1919          bts2rd,
1920          create_node,
[6c988987]1921          lfn_entries,
[d2e0bb3]1922          name_dir_entry,
1923          dir_pos,
[b1daf0f]1924          &empty_file_offset,
1925          &empty_entry_count);
[d2e0bb3]1926    }
1927    /* Create a non-existing file/directory if requested */
1928    if (   retval == RC_OK
1929        && create_node) {
1930        switch (name_type) {
1931          case MSDOS_NAME_SHORT:
1932              name_len_for_save = msdos_filename_utf8_to_short_name_for_save (
1933                  converter,
1934                  name_utf8,
1935                  name_utf8_len,
1936                  buffer,
1937                  MSDOS_SHORT_NAME_LEN);
1938              if (name_len_for_save > 0 ) {
[6c988987]1939                  lfn_entries = 0;
[d2e0bb3]1940              }
1941              else
1942                  retval = -1;
1943          break;
1944          case MSDOS_NAME_LONG:
1945              name_len_for_save = msdos_filename_utf8_to_long_name_for_save (
1946                  converter,
1947                  name_utf8,
1948                  name_utf8_len,
1949                  buffer,
1950                  buffer_size);
1951              if (name_len_for_save > 0) {
[6c988987]1952                  lfn_entries = (name_len_for_save + MSDOS_LFN_ENTRY_SIZE - 1)
[83a4cbb]1953                    / MSDOS_LFN_ENTRY_SIZE;
[d2e0bb3]1954              }
1955              else
1956                  retval = -1;
1957          break;
1958          default:
1959              errno = EINVAL;
1960              retval = -1;
1961          break;
1962        }
[4c9f02d]1963
1964        if (retval == RC_OK)
1965            retval = msdos_add_file (
1966                buffer,
1967                name_type,
1968                fs_info,
1969                fat_fd,
1970                bts2rd,
[6c988987]1971                lfn_entries,
[4c9f02d]1972                name_dir_entry,
1973                dir_pos,
[b1daf0f]1974                empty_file_offset,
1975                empty_entry_count
[4c9f02d]1976            );
[d2e0bb3]1977    }
1978
1979    return retval;
[f36a7bfc]1980}
1981
[d2e0bb3]1982
[f36a7bfc]1983/* msdos_find_node_by_cluster_num_in_fat_file --
1984 *     Find node with specified number of cluster in fat-file.
1985 *
[07d6fd5]1986 * Note, not updated in the LFN change because it is only used
1987 *       for . and .. entries and these are always short.
1988 *
[f36a7bfc]1989 * PARAMETERS:
[a5305f6b]1990 *     mt_entry  - mount table entry
1991 *     fat_fd    - fat-file descriptor
1992 *     cl4find   - number of cluster to find
[f36a7bfc]1993 *     paux      - identify a node location on the disk -
1994 *                 cluster num and offset inside the cluster
1995 *     dir_entry - placeholder for found node
1996 *
1997 * RETURNS:
1998 *     RC_OK on success, or error code if error occured
1999 *
2000 */
[e197621]2001int msdos_find_node_by_cluster_num_in_fat_file(
[f36a7bfc]2002    rtems_filesystem_mount_table_entry_t *mt_entry,
2003    fat_file_fd_t                        *fat_fd,
[a5305f6b]2004    uint32_t                              cl4find,
[07d6fd5]2005    fat_dir_pos_t                        *dir_pos,
[f36a7bfc]2006    char                                 *dir_entry
2007    )
2008{
[e197621]2009    int              rc = RC_OK;
[f36a7bfc]2010    ssize_t          ret = 0;
2011    msdos_fs_info_t *fs_info = mt_entry->fs_info;
[a5305f6b]2012    uint32_t         bts2rd = 0;
[f91bbe64]2013    uint32_t         i = 0, j = 0;
[f36a7bfc]2014
[a5305f6b]2015    if (FAT_FD_OF_ROOT_DIR(fat_fd) &&
[f36a7bfc]2016       (fs_info->fat.vol.type & (FAT_FAT12 | FAT_FAT16)))
[a5305f6b]2017        bts2rd = fat_fd->fat_file_size;
[f36a7bfc]2018    else
2019        bts2rd = fs_info->fat.vol.bpc;
2020
[c65afce4]2021    while ((ret = fat_file_read(&fs_info->fat, fat_fd, j * bts2rd, bts2rd,
[f36a7bfc]2022                                  fs_info->cl_buf)) != FAT_EOF)
2023    {
2024        if ( ret < MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE )
[3899a537]2025            rtems_set_errno_and_return_minus_one( EIO );
[f36a7bfc]2026
2027        assert(ret == bts2rd);
[a5305f6b]2028
[f36a7bfc]2029        for (i = 0; i < bts2rd; i += MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE)
2030        {
[07d6fd5]2031            char* entry = (char*) fs_info->cl_buf + i;
[0a7278e]2032
[f36a7bfc]2033            /* if this and all rest entries are empty - return not-found */
[07d6fd5]2034            if ((*MSDOS_DIR_ENTRY_TYPE(entry)) ==
[f36a7bfc]2035                MSDOS_THIS_DIR_ENTRY_AND_REST_EMPTY)
2036                return MSDOS_NAME_NOT_FOUND_ERR;
2037
[a5305f6b]2038            /* if this entry is empty - skip it */
[07d6fd5]2039            if ((*MSDOS_DIR_ENTRY_TYPE(entry)) ==
[f36a7bfc]2040                MSDOS_THIS_DIR_ENTRY_EMPTY)
2041                continue;
2042
2043            /* if get a non-empty entry - compare clusters num */
[07d6fd5]2044            if (MSDOS_EXTRACT_CLUSTER_NUM(entry) == cl4find)
[f36a7bfc]2045            {
2046                /* on success fill aux structure and copy all 32 bytes */
[c65afce4]2047                rc = fat_file_ioctl(&fs_info->fat, fat_fd, F_CLU_NUM, j * bts2rd,
[07d6fd5]2048                                    &dir_pos->sname.cln);
[f36a7bfc]2049                if (rc != RC_OK)
2050                    return rc;
2051
[07d6fd5]2052                dir_pos->sname.ofs = i;
2053                dir_pos->lname.cln = FAT_FILE_SHORT_NAME;
2054                dir_pos->lname.ofs = FAT_FILE_SHORT_NAME;
[0a7278e]2055
[07d6fd5]2056                memcpy(dir_entry, entry,
[f36a7bfc]2057                       MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE);
2058                return RC_OK;
2059            }
[a5305f6b]2060        }
[f36a7bfc]2061        j++;
[a5305f6b]2062    }
[f36a7bfc]2063    return MSDOS_NAME_NOT_FOUND_ERR;
2064}
[86ef0df]2065
2066int
2067msdos_sync(rtems_libio_t *iop)
2068{
2069    int                rc = RC_OK;
2070    rtems_status_code  sc = RTEMS_SUCCESSFUL;
2071    msdos_fs_info_t   *fs_info = iop->pathinfo.mt_entry->fs_info;
2072
2073    sc = rtems_semaphore_obtain(fs_info->vol_sema, RTEMS_WAIT,
2074                                MSDOS_VOLUME_SEMAPHORE_TIMEOUT);
2075    if (sc != RTEMS_SUCCESSFUL)
2076        rtems_set_errno_and_return_minus_one(EIO);
2077
[0f0db894]2078    rc = fat_sync(&fs_info->fat);
[86ef0df]2079
2080    rtems_semaphore_release(fs_info->vol_sema);
2081    return rc;
2082}
Note: See TracBrowser for help on using the repository browser.