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

4.115
Last change on this file since 4c9f02d was 4c9f02d, checked in by Sebastian Huber <sebastian.huber@…>, on 09/11/14 at 11:04:42

dosfs: Check error status

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