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

4.115
Last change on this file since 92422be was 92422be, checked in by Gedare Bloom <gedare@…>, on 03/10/15 at 13:54:30

dosfs: avoid buffer-overread. closes #2292.

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