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

4.115
Last change on this file since a7eaaae8 was a7eaaae8, checked in by Sebastian Huber <sebastian.huber@…>, on Oct 20, 2014 at 7:33:34 AM

dosfs: Support ctime and mtime

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

  • Property mode set to 100644
File size: 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                 (    entry_string_buf[chars_in_entry] != 0x0000
1059                  && chars_in_entry < MSDOS_LFN_LEN_PER_ENTRY );
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.