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

4.115
Last change on this file since 3ae5950 was 3ae5950, checked in by Sebastian Huber <sebastian.huber@…>, on 12/28/10 at 08:46:57

2010-12-28 Sebastian Huber <sebastian.huber@…>

  • libfs/src/dosfs/msdos_misc.c: Create short file names acceptable for Windows.
  • Property mode set to 100644
File size: 54.6 KB
Line 
1/*
2 *  Miscellaneous routines implementation for MSDOS filesystem
3 *
4 *  Copyright (C) 2001 OKTET Ltd., St.-Petersburg, Russia
5 *  Author: Eugeny S. Mints <Eugeny.Mints@oktet.ru>
6 *
7 *  The license and distribution terms for this file may be
8 *  found in the file LICENSE in this distribution or at
9 *  http://www.rtems.com/license/LICENSE.
10 *
11 *  @(#) $Id$
12 */
13
14#define MSDOS_TRACE 1
15
16#if HAVE_CONFIG_H
17#include "config.h"
18#endif
19
20#include <stdlib.h>
21#include <ctype.h>
22#include <sys/time.h>
23#include <unistd.h>
24#include <string.h>
25#ifdef HAVE_STRINGS_H
26#include <strings.h>
27#endif
28#include <assert.h>
29#include <rtems/libio_.h>
30
31#include "fat.h"
32#include "fat_fat_operations.h"
33#include "fat_file.h"
34
35#include "msdos.h"
36
37
38#include <stdio.h>
39
40/*
41 * External strings. Saves spave this way.
42 */
43const char *const MSDOS_DOT_NAME    = ".          ";
44const char *const MSDOS_DOTDOT_NAME = "..         ";
45
46/* msdos_is_valid_name_char --
47 *     Routine to check the character in a file or directory name.
48 *     The characters support in the short file name are letters,
49 *     digits, or characters with code points values greater than
50 *     127 (not sure what this last is) plus the following special
51 *     characters "$%'-_@~`!(){}^#&". The must be uppercase.
52 *
53 *     The following 6 characters are allowed in a long names,
54 *     " +,;=[]" including a space and lower case letters.
55 *
56 * PARAMETERS:
57 *     ch        - character to check.
58 *
59 * RETURNS:
60 *     MSDOS_NAME_INVALID - Not valid in a long or short name.
61 *     MSDOS_NAME_SHORT   - Valid in a short name or long name.
62 *     MSDOS_NAME_LONG    - Valid in a long name only.
63 *
64 */
65static msdos_name_type_t
66msdos_is_valid_name_char(const char ch)
67{
68    if (strchr(" +,;=[]", ch) != NULL)
69        return MSDOS_NAME_LONG;
70
71    if ((ch == '.') || isalnum((unsigned char)ch) ||
72        (strchr("$%'-_@~`!(){}^#&", ch) != NULL))
73        return MSDOS_NAME_SHORT;
74
75    return MSDOS_NAME_INVALID;
76}
77
78/* msdos_short_hex_number --
79 *     Routine to set the hex number in the SFN.
80 *
81 * PARAMETERS:
82 *     name      - name to change
83 *     num       - number to set
84 *
85 * RETURNS:
86 *     nothing
87 *
88 */
89static void
90msdos_short_name_hex(char* sfn, int num)
91{
92    static const char* hex = "0123456789ABCDEF";
93    char* c = MSDOS_DIR_NAME(sfn);
94    int   i;
95    for (i = 0; i < 2; i++, c++)
96      if ((*c == ' ') || (*c == '.'))
97        *c = '_';
98    for (i = 0; i < 4; i++, c++)
99      *c = hex[(num >> ((3 - i) * 4)) & 0xf];
100    *c++ = '~';
101    *c++ = '1';
102}
103
104/* msdos_name_type --
105 *     Routine the type of file name.
106 *
107 * PARAMETERS:
108 *     name      - name to check
109 *
110 * RETURNS:
111 *     true the name is long, else the name is short.
112 *
113 */
114#define MSDOS_NAME_TYPE_PRINT 0
115static msdos_name_type_t
116msdos_name_type(const char *name, int name_len)
117{
118    bool lowercase = false;
119    bool uppercase = false;
120    int  dot_at = -1;
121    int  count = 0;
122
123    while (*name && (count < name_len))
124    {
125        bool is_dot = *name == '.';
126        msdos_name_type_t type = msdos_is_valid_name_char(*name);
127
128#if MSDOS_NAME_TYPE_PRINT
129        printf ("MSDOS_NAME_TYPE: c:%02x type:%d\n", *name, type);
130#endif
131       
132        if ((type == MSDOS_NAME_INVALID) || (type == MSDOS_NAME_LONG))
133            return type;
134
135        if (dot_at >= 0)
136        {
137            if (is_dot || ((count - dot_at) > 3))
138            {
139#if MSDOS_NAME_TYPE_PRINT
140                printf ("MSDOS_NAME_TYPE: LONG[1]: is_dot:%d, at:%d cnt\n",
141                        is_dot, dot_at, count);
142#endif
143                return MSDOS_NAME_LONG;
144            }
145        }
146        else
147        {
148            if (count == 8 && !is_dot)
149            {
150#if MSDOS_NAME_TYPE_PRINT
151                printf ("MSDOS_NAME_TYPE: LONG[2]: is_dot:%d, at:%d cnt\n",
152                        is_dot, dot_at, count);
153#endif
154                return MSDOS_NAME_LONG;
155            }
156        }
157
158        if (is_dot)
159            dot_at = count;
160        else if ((*name >= 'A') && (*name <= 'Z'))
161            uppercase = true;
162        else if ((*name >= 'a') && (*name <= 'z'))
163            lowercase = true;
164
165        count++;
166        name++;
167    }
168
169    if (lowercase && uppercase)
170    {
171#if MSDOS_NAME_TYPE_PRINT
172        printf ("MSDOS_NAME_TYPE: LONG[3]\n");
173#endif
174        return MSDOS_NAME_LONG;
175    }
176   
177#if MSDOS_NAME_TYPE_PRINT
178    printf ("MSDOS_NAME_TYPE: SHORT[1]\n");
179#endif
180    return MSDOS_NAME_SHORT;
181}
182
183/* msdos_long_to_short --
184 *     Routine to creates a short name from a long. Start the end of the
185 *
186 * PARAMETERS:
187 *     name      - name to check
188 *
189 * RETURNS:
190 *     true the name is long, else the name is short.
191 *
192 */
193#define MSDOS_L2S_PRINT 0
194msdos_name_type_t
195msdos_long_to_short(const char *lfn, int lfn_len, char* sfn, int sfn_len)
196{
197    msdos_name_type_t type;
198    int               i;
199
200    /*
201     * Fill with spaces. This is how a short directory entry is padded.
202     */
203    memset (sfn, ' ', sfn_len);
204
205    /*
206     * Handle '.' and '..' specially.
207     */
208    if ((lfn[0] == '.') && (lfn_len == 1))
209    {
210        sfn[0] = '.';
211#if MSDOS_L2S_PRINT
212        printf ("MSDOS_L2S: SHORT[1]: lfn:'%s' SFN:'%s'\n", lfn, sfn);
213#endif
214        return MSDOS_NAME_SHORT;
215    }
216
217    if ((lfn[0] == '.') && (lfn[1] == '.') && (lfn_len == 2))
218    {
219        sfn[0] = sfn[1] = '.';
220#if MSDOS_L2S_PRINT
221        printf ("MSDOS_L2S: SHORT[2]: lfn:'%s' SFN:'%s'\n", lfn, sfn);
222#endif
223        return MSDOS_NAME_SHORT;
224    }
225
226    /*
227     * Filenames with only blanks and dots are not allowed!
228     */
229    for (i = 0; i < lfn_len; i++)
230        if ((lfn[i] != ' ') && (lfn[i] != '.'))
231            break;
232
233    if (i == lfn_len)
234    {
235#if MSDOS_L2S_PRINT
236        printf ("MSDOS_L2S: INVALID[1]: lfn:'%s' SFN:'%s'\n", lfn, sfn);
237#endif
238        return MSDOS_NAME_INVALID;
239    }
240
241    /*
242     * Is this a short name ?
243     */
244
245    type = msdos_name_type (lfn, lfn_len);
246
247    if (type == MSDOS_NAME_INVALID)
248    {
249#if MSDOS_L2S_PRINT
250        printf ("MSDOS_L2S: INVALID[2]: lfn:'%s' SFN:'%s'\n", lfn, sfn);
251#endif
252        return MSDOS_NAME_INVALID;
253    }
254
255    msdos_filename_unix2dos (lfn, lfn_len, sfn);
256
257#if MSDOS_L2S_PRINT
258    printf ("MSDOS_L2S: TYPE:%d lfn:'%s' SFN:'%s'\n", type, lfn, sfn);
259#endif
260    return type;
261}
262
263/* msdos_get_token --
264 *     Routine to get a token (name or separator) from the path.
265 *
266 * PARAMETERS:
267 *     path      - path to get token from
268 *     ret_token - returned token
269 *     token_len - length of returned token
270 *
271 * RETURNS:
272 *     token type, token and token length
273 *
274 */
275msdos_token_types_t
276msdos_get_token(const char  *path,
277                int          pathlen,
278                const char **ret_token,
279                int         *ret_token_len)
280{
281    msdos_token_types_t type = MSDOS_NAME;
282    int                 i = 0;
283
284    *ret_token = NULL;
285    *ret_token_len = 0;
286
287    if (pathlen == 0)
288        return MSDOS_NO_MORE_PATH;
289   
290    /*
291     *  Check for a separator.
292     */
293    while (!msdos_is_separator(path[i]) && (i < pathlen))
294    {
295        if ( !msdos_is_valid_name_char(path[i]) )
296            return MSDOS_INVALID_TOKEN;
297        ++i;
298        if ( i == MSDOS_NAME_MAX_LFN_WITH_DOT )
299            return MSDOS_INVALID_TOKEN;
300    }
301
302    *ret_token = path;
303
304    /*
305     *  If it is just a separator then it is the current dir.
306     */
307    if ( i == 0 )
308    {
309      if ( (*path != '\0') && pathlen )
310        {
311            i++;
312            type = MSDOS_CURRENT_DIR;
313        }
314        else
315            type = MSDOS_NO_MORE_PATH;
316    }
317
318    /*
319     *  Set the token and token_len to the token start and length.
320     */
321    *ret_token_len = i;
322
323    /*
324     *  If we copied something that was not a seperator see if
325     *  it was a special name.
326     */
327    if ( type == MSDOS_NAME )
328    {
329        if ((i == 2) && ((*ret_token)[0] == '.') && ((*ret_token)[1] == '.'))
330        {
331            type = MSDOS_UP_DIR;
332            return type;
333        }
334
335        if ((i == 1) && ((*ret_token)[0] == '.'))
336        {
337            type = MSDOS_CURRENT_DIR;
338            return type;
339        }
340    }
341
342    return type;
343}
344
345
346/* msdos_find_name --
347 *     Find the node which correspondes to the name, open fat-file which
348 *     correspondes to the found node and close fat-file which correspondes
349 *     to the node we searched in.
350 *
351 * PARAMETERS:
352 *     parent_loc - parent node description
353 *     name       - name to find
354 *
355 * RETURNS:
356 *     RC_OK and updated 'parent_loc' on success, or -1 if error
357 *     occured (errno set apropriately)
358 *
359 */
360int
361msdos_find_name(
362    rtems_filesystem_location_info_t *parent_loc,
363    const char                       *name,
364    int                               name_len
365    )
366{
367    int                rc = RC_OK;
368    msdos_fs_info_t   *fs_info = parent_loc->mt_entry->fs_info;
369    fat_file_fd_t     *fat_fd = NULL;
370    msdos_name_type_t  name_type;
371    fat_dir_pos_t      dir_pos;
372    unsigned short     time_val = 0;
373    unsigned short     date = 0;
374    char               node_entry[MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE];
375
376    memset(node_entry, 0, MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE);
377
378    name_type = msdos_long_to_short (name,
379                                     name_len,
380                                     MSDOS_DIR_NAME(node_entry),
381                                     MSDOS_NAME_MAX);
382
383    /*
384     * find the node which correspondes to the name in the directory pointed by
385     * 'parent_loc'
386     */
387    rc = msdos_get_name_node(parent_loc, false, name, name_len, name_type,
388                             &dir_pos, node_entry);
389    if (rc != RC_OK)
390        return rc;
391
392    if (((*MSDOS_DIR_ATTR(node_entry)) & MSDOS_ATTR_VOLUME_ID) ||
393        ((*MSDOS_DIR_ATTR(node_entry) & MSDOS_ATTR_LFN_MASK) == MSDOS_ATTR_LFN))
394        return MSDOS_NAME_NOT_FOUND_ERR;
395
396    /* open fat-file corresponded to the found node */
397    rc = fat_file_open(parent_loc->mt_entry, &dir_pos, &fat_fd);
398    if (rc != RC_OK)
399        return rc;
400
401    fat_fd->dir_pos = dir_pos;
402
403    /*
404     * I don't like this if, but: we should do it, or should write new file
405     * size and first cluster num to the disk after each write operation
406     * (even if one byte is written  - that is TOO slow) because
407     * otherwise real values of these fields stored in fat-file descriptor
408     * may be accidentally rewritten with wrong values stored on the disk
409     */
410    if (fat_fd->links_num == 1)
411    {
412        fat_fd->cln = MSDOS_EXTRACT_CLUSTER_NUM(node_entry);
413
414        time_val = *MSDOS_DIR_WRITE_TIME(node_entry);
415        date = *MSDOS_DIR_WRITE_DATE(node_entry);
416
417        fat_fd->mtime = msdos_date_dos2unix(CF_LE_W(date), CF_LE_W(time_val));
418
419        if ((*MSDOS_DIR_ATTR(node_entry)) & MSDOS_ATTR_DIRECTORY)
420        {
421            fat_fd->fat_file_type = FAT_DIRECTORY;
422            fat_fd->size_limit = MSDOS_MAX_DIR_LENGHT;
423
424            rc = fat_file_size(parent_loc->mt_entry, fat_fd);
425            if (rc != RC_OK)
426            {
427                fat_file_close(parent_loc->mt_entry, fat_fd);
428                return rc;
429            }
430        }
431        else
432        {
433            fat_fd->fat_file_size = CF_LE_L(*MSDOS_DIR_FILE_SIZE(node_entry));
434            fat_fd->fat_file_type = FAT_FILE;
435            fat_fd->size_limit = MSDOS_MAX_FILE_SIZE;
436        }
437
438        /* these data is not actual for zero-length fat-file */
439        fat_fd->map.file_cln = 0;
440        fat_fd->map.disk_cln = fat_fd->cln;
441
442        if ((fat_fd->fat_file_size != 0) &&
443            (fat_fd->fat_file_size <= fs_info->fat.vol.bpc))
444        {
445            fat_fd->map.last_cln = fat_fd->cln;
446        }
447        else
448        {
449            fat_fd->map.last_cln = FAT_UNDEFINED_VALUE;
450        }
451    }
452
453    /* close fat-file corresponded to the node we searched in */
454    rc = fat_file_close(parent_loc->mt_entry, parent_loc->node_access);
455    if (rc != RC_OK)
456    {
457        fat_file_close(parent_loc->mt_entry, fat_fd);
458        return rc;
459    }
460
461    /* update node_info_ptr field */
462    parent_loc->node_access = fat_fd;
463
464    return rc;
465}
466
467/* msdos_get_name_node --
468 *     This routine is used in two ways: for a new node creation (a) or for
469 *     search the node which correspondes to the name parameter (b).
470 *     In case (a) 'name' should be set up to NULL and 'name_dir_entry' should
471 *     point to initialized 32 bytes structure described a new node.
472 *     In case (b) 'name' should contain a valid string.
473 *
474 *     (a): reading fat-file which correspondes to directory we are going to
475 *          create node in. If free slot is found write contents of
476 *          'name_dir_entry' into it. If reach end of fat-file and no free
477 *          slot found, write 32 bytes to the end of fat-file.
478 *
479 *     (b): reading fat-file which correspondes to directory and trying to
480 *          find slot with the name field == 'name' parameter
481 *
482 *
483 * PARAMETERS:
484 *     parent_loc     - node description to create node in or to find name in
485 *     name           - NULL or name to find
486 *     paux           - identify a node location on the disk -
487 *                      cluster num and offset inside the cluster
488 *     short_dir_entry - node to create/placeholder for found node (IN/OUT)
489 *
490 * RETURNS:
491 *     RC_OK, filled aux_struct_ptr and name_dir_entry on success, or -1 if
492 *     error occured (errno set apropriately)
493 *
494 */
495int
496msdos_get_name_node(
497    rtems_filesystem_location_info_t *parent_loc,
498    bool                              create_node,
499    const char                       *name,
500    int                               name_len,
501    msdos_name_type_t                 name_type,
502    fat_dir_pos_t                    *dir_pos,
503    char                             *name_dir_entry
504    )
505{
506    int              rc = RC_OK;
507    fat_file_fd_t   *fat_fd = parent_loc->node_access;
508    uint32_t         dotdot_cln = 0;
509
510    /* find name in fat-file which corresponds to the directory */
511    rc = msdos_find_name_in_fat_file(parent_loc->mt_entry, fat_fd,
512                                     create_node, name, name_len, name_type,
513                                     dir_pos, name_dir_entry);
514    if ((rc != RC_OK) && (rc != MSDOS_NAME_NOT_FOUND_ERR))
515        return rc;
516
517    if (!create_node)
518    {
519        /* if we search for valid name and name not found -> return */
520        if (rc == MSDOS_NAME_NOT_FOUND_ERR)
521            return rc;
522
523        /*
524         * if we have deal with ".." - it is a special case :(((
525         *
526         * Really, we should return cluster num and offset not of ".." slot, but
527         * slot which correspondes to real directory name.
528         */
529        if (rc == RC_OK)
530        {
531            if (strncmp(name, "..", 2) == 0)
532            {
533                dotdot_cln = MSDOS_EXTRACT_CLUSTER_NUM((name_dir_entry));
534
535                /* are we right under root dir ? */
536                if (dotdot_cln == 0)
537                {
538                    /*
539                     * we can relax about first_char field - it never should be
540                     * used for root dir
541                     */
542                    fat_dir_pos_init(dir_pos);
543                    dir_pos->sname.cln = FAT_ROOTDIR_CLUSTER_NUM;
544                }
545                else
546                {
547                    rc =
548                        msdos_get_dotdot_dir_info_cluster_num_and_offset(parent_loc->mt_entry,
549                                                                         dotdot_cln,
550                                                                         dir_pos,
551                                                                         name_dir_entry);
552                    if (rc != RC_OK)
553                        return rc;
554                }
555            }
556        }
557    }
558    return rc;
559}
560
561/*
562 * msdos_get_dotdot_dir_info_cluster_num_and_offset
563 *
564 * Unfortunately, in general, we cann't work here in fat-file ideologic
565 * (open fat_file "..", get ".." and ".", open "..", find an entry ...)
566 * because if we open
567 * fat-file ".." it may happend that we have two different fat-file
568 * descriptors ( for real name of directory and ".." name ) for a single
569 * file  ( cluster num of both pointers to the same cluster )
570 * But...we do it because we protected by semaphore
571 *
572 */
573
574/* msdos_get_dotdot_dir_info_cluster_num_and_offset --
575 *     Get cluster num and offset not of ".." slot, but slot which correspondes
576 *     to real directory name.
577 *
578 * PARAMETERS:
579 *     mt_entry       - mount table entry
580 *     cln            - data cluster num extracted drom ".." slot
581 *     paux           - identify a node location on the disk -
582 *                      number of cluster and offset inside the cluster
583 *     dir_entry      - placeholder for found node
584 *
585 * RETURNS:
586 *     RC_OK, filled 'paux' and 'dir_entry' on success, or -1 if error occured
587 *     (errno set apropriately)
588 *
589 */
590int
591msdos_get_dotdot_dir_info_cluster_num_and_offset(
592    rtems_filesystem_mount_table_entry_t *mt_entry,
593    uint32_t                              cln,
594    fat_dir_pos_t                        *dir_pos,
595    char                                 *dir_entry
596    )
597{
598    int              rc = RC_OK;
599    msdos_fs_info_t *fs_info = mt_entry->fs_info;
600    fat_file_fd_t   *fat_fd = NULL;
601    char             dot_node[MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE];
602    char             dotdot_node[MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE];
603    uint32_t         cl4find = 0;
604
605    /*
606     * open fat-file corresponded to ".."
607     */
608    rc = fat_file_open(mt_entry, dir_pos, &fat_fd);
609    if (rc != RC_OK)
610        return rc;
611
612    fat_fd->cln = cln;
613    fat_fd->fat_file_type = FAT_DIRECTORY;
614    fat_fd->size_limit = MSDOS_MAX_DIR_LENGHT;
615
616    fat_fd->map.file_cln = 0;
617    fat_fd->map.disk_cln = fat_fd->cln;
618
619    rc = fat_file_size(mt_entry, fat_fd);
620    if (rc != RC_OK)
621    {
622        fat_file_close(mt_entry, fat_fd);
623        return rc;
624    }
625
626    /* find "." node in opened directory */
627    memset(dot_node, 0, MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE);
628    msdos_long_to_short(".", 1, dot_node, MSDOS_SHORT_NAME_LEN);
629    rc = msdos_find_name_in_fat_file(mt_entry, fat_fd, false, ".", 1,
630                                     MSDOS_NAME_SHORT, dir_pos, dot_node);
631
632    if (rc != RC_OK)
633    {
634        fat_file_close(mt_entry, fat_fd);
635        return rc;
636    }
637
638    /* find ".." node in opened directory */
639    memset(dotdot_node, 0, MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE);
640    msdos_long_to_short("..", 2, dotdot_node, MSDOS_SHORT_NAME_LEN);
641    rc = msdos_find_name_in_fat_file(mt_entry, fat_fd, false, "..", 2,
642                                     MSDOS_NAME_SHORT, dir_pos,
643                                     dotdot_node);
644
645    if (rc != RC_OK)
646    {
647        fat_file_close(mt_entry, fat_fd);
648        return rc;
649    }
650
651    cl4find = MSDOS_EXTRACT_CLUSTER_NUM(dot_node);
652
653    /* close fat-file corresponded to ".." directory */
654    rc = fat_file_close(mt_entry, fat_fd);
655    if ( rc != RC_OK )
656        return rc;
657
658    if ( (MSDOS_EXTRACT_CLUSTER_NUM(dotdot_node)) == 0)
659    {
660        /*
661         * we handle root dir for all FAT types in the same way with the
662         * ordinary directories ( through fat_file_* calls )
663         */
664        fat_dir_pos_init(dir_pos);
665        dir_pos->sname.cln = FAT_ROOTDIR_CLUSTER_NUM;
666    }
667
668    /* open fat-file corresponded to second ".." */
669    rc = fat_file_open(mt_entry, dir_pos, &fat_fd);
670    if (rc != RC_OK)
671        return rc;
672
673    if ((MSDOS_EXTRACT_CLUSTER_NUM(dotdot_node)) == 0)
674        fat_fd->cln = fs_info->fat.vol.rdir_cl;
675    else
676        fat_fd->cln = MSDOS_EXTRACT_CLUSTER_NUM(dotdot_node);
677
678    fat_fd->fat_file_type = FAT_DIRECTORY;
679    fat_fd->size_limit = MSDOS_MAX_DIR_LENGHT;
680
681    fat_fd->map.file_cln = 0;
682    fat_fd->map.disk_cln = fat_fd->cln;
683
684    rc = fat_file_size(mt_entry, fat_fd);
685    if (rc != RC_OK)
686    {
687        fat_file_close(mt_entry, fat_fd);
688        return rc;
689    }
690
691    /* in this directory find slot with specified cluster num */
692    rc = msdos_find_node_by_cluster_num_in_fat_file(mt_entry, fat_fd, cl4find,
693                                                    dir_pos, dir_entry);
694    if (rc != RC_OK)
695    {
696        fat_file_close(mt_entry, fat_fd);
697        return rc;
698    }
699    rc = fat_file_close(mt_entry, fat_fd);
700    return rc;
701}
702
703
704/* msdos_set_dir_wrt_time_and_date --
705 *     Write last write date and time for a file to the disk (to corresponded
706 *     32bytes node)
707 *
708 * PARAMETERS:
709 *     mt_entry - mount table entry
710 *     fat_fd   - fat-file descriptor
711 *
712 * RETURNS:
713 *     RC_OK on success, or -1 if error occured (errno set apropriately).
714 *
715 */
716int
717msdos_set_dir_wrt_time_and_date(
718    rtems_filesystem_mount_table_entry_t *mt_entry,
719    fat_file_fd_t                        *fat_fd
720    )
721{
722    ssize_t          ret1 = 0, ret2 = 0, ret3 = 0;
723    msdos_fs_info_t *fs_info = mt_entry->fs_info;
724    uint16_t         time_val;
725    uint16_t         date;
726    uint32_t         sec = 0;
727    uint32_t         byte = 0;
728
729    msdos_date_unix2dos(fat_fd->mtime, &date, &time_val);
730
731    /*
732     * calculate input for _fat_block_write: convert (cluster num, offset) to
733     * (sector num, new offset)
734     */
735    sec = fat_cluster_num_to_sector_num(mt_entry, fat_fd->dir_pos.sname.cln);
736    sec += (fat_fd->dir_pos.sname.ofs >> fs_info->fat.vol.sec_log2);
737    /* byte points to start of 32bytes structure */
738    byte = fat_fd->dir_pos.sname.ofs & (fs_info->fat.vol.bps - 1);
739
740    time_val = CT_LE_W(time_val);
741    ret1 = _fat_block_write(mt_entry, sec, byte + MSDOS_FILE_WTIME_OFFSET,
742                            2, (char *)(&time_val));
743    date = CT_LE_W(date);
744    ret2 = _fat_block_write(mt_entry, sec, byte + MSDOS_FILE_WDATE_OFFSET,
745                            2, (char *)(&date));
746    ret3 = _fat_block_write(mt_entry, sec, byte + MSDOS_FILE_ADATE_OFFSET,
747                            2, (char *)(&date));
748
749    if ( (ret1 < 0) || (ret2 < 0) || (ret3 < 0) )
750        return -1;
751
752    return RC_OK;
753}
754
755/* msdos_set_first_cluster_num --
756 *     Write number of first cluster of the file to the disk (to corresponded
757 *     32bytes slot)
758 *
759 * PARAMETERS:
760 *     mt_entry - mount table entry
761 *     fat_fd   - fat-file descriptor
762 *
763 * RETURNS:
764 *     RC_OK on success, or -1 if error occured
765 *
766 */
767int
768msdos_set_first_cluster_num(
769    rtems_filesystem_mount_table_entry_t *mt_entry,
770    fat_file_fd_t                        *fat_fd
771    )
772{
773    ssize_t          ret1 = 0, ret2 = 0;
774    msdos_fs_info_t *fs_info = mt_entry->fs_info;
775    uint32_t         new_cln = fat_fd->cln;
776    uint16_t         le_cl_low = 0;
777    uint16_t         le_cl_hi = 0;
778    uint32_t         sec = 0;
779    uint32_t         byte = 0;
780
781    /*
782     * calculate input for _fat_block_write: convert (cluster num, offset) to
783     * (sector num, new offset)
784     */
785    sec = fat_cluster_num_to_sector_num(mt_entry, fat_fd->dir_pos.sname.cln);
786    sec += (fat_fd->dir_pos.sname.ofs >> fs_info->fat.vol.sec_log2);
787    /* byte from points to start of 32bytes structure */
788    byte = fat_fd->dir_pos.sname.ofs & (fs_info->fat.vol.bps - 1);
789
790    le_cl_low = CT_LE_W((uint16_t  )(new_cln & 0x0000FFFF));
791    ret1 = _fat_block_write(mt_entry, sec,
792                            byte + MSDOS_FIRST_CLUSTER_LOW_OFFSET, 2,
793                            (char *)(&le_cl_low));
794    le_cl_hi = CT_LE_W((uint16_t  )((new_cln & 0xFFFF0000) >> 16));
795    ret2 = _fat_block_write(mt_entry, sec,
796                            byte + MSDOS_FIRST_CLUSTER_HI_OFFSET, 2,
797                            (char *)(&le_cl_hi));
798    if ( (ret1 < 0) || (ret2 < 0) )
799        return -1;
800
801    return RC_OK;
802}
803
804
805/* msdos_set_file size --
806 *     Write file size of the file to the disk (to corresponded 32bytes slot)
807 *
808 * PARAMETERS:
809 *     mt_entry - mount table entry
810 *     fat_fd   - fat-file descriptor
811 *
812 * RETURNS:
813 *     RC_OK on success, or -1 if error occured (errno set apropriately).
814 *
815 */
816int
817msdos_set_file_size(
818    rtems_filesystem_mount_table_entry_t *mt_entry,
819    fat_file_fd_t                        *fat_fd
820    )
821{
822    ssize_t          ret = 0;
823    msdos_fs_info_t *fs_info = mt_entry->fs_info;
824    uint32_t         le_new_length = 0;
825    uint32_t         sec = 0;
826    uint32_t         byte = 0;
827
828    sec = fat_cluster_num_to_sector_num(mt_entry, fat_fd->dir_pos.sname.cln);
829    sec += (fat_fd->dir_pos.sname.ofs >> fs_info->fat.vol.sec_log2);
830    byte = (fat_fd->dir_pos.sname.ofs & (fs_info->fat.vol.bps - 1));
831
832    le_new_length = CT_LE_L((fat_fd->fat_file_size));
833    ret = _fat_block_write(mt_entry, sec, byte + MSDOS_FILE_SIZE_OFFSET, 4,
834                           (char *)(&le_new_length));
835    if ( ret < 0 )
836        return -1;
837
838    return RC_OK;
839}
840
841/*
842 * We should not check whether this routine is called for root dir - it
843 * never can happend
844 */
845
846/* msdos_set_first_char4file_name --
847 *     Write first character of the name of the file to the disk (to
848 *     corresponded 32bytes slot)
849 *
850 * PARAMETERS:
851 *     mt_entry - mount table entry
852 *     cl       - number of cluster
853 *     ofs      - offset inside cluster
854 *     fchar    - character to set up
855 *
856 * RETURNS:
857 *     RC_OK on success, or -1 if error occured (errno set apropriately)
858 *
859 */
860int
861msdos_set_first_char4file_name(
862    rtems_filesystem_mount_table_entry_t *mt_entry,
863    fat_dir_pos_t                        *dir_pos,
864    unsigned char                         fchar
865    )
866{
867    ssize_t          ret;
868    msdos_fs_info_t *fs_info = mt_entry->fs_info;
869    uint32_t         dir_block_size;
870    fat_pos_t        start = dir_pos->lname;
871    fat_pos_t        end = dir_pos->sname;
872
873    if ((end.cln == fs_info->fat.vol.rdir_cl) &&
874        (fs_info->fat.vol.type & (FAT_FAT12 | FAT_FAT16)))
875      dir_block_size = fs_info->fat.vol.rdir_size;
876    else
877      dir_block_size = fs_info->fat.vol.bpc;
878
879    if (dir_pos->lname.cln == FAT_FILE_SHORT_NAME)
880      start = dir_pos->sname;
881
882    /*
883     * We handle the changes directly due the way the short file
884     * name code was written rather than use the fat_file_write
885     * interface.
886     */
887    while (true)
888    {
889      uint32_t sec = (fat_cluster_num_to_sector_num(mt_entry, start.cln) +
890                      (start.ofs >> fs_info->fat.vol.sec_log2));
891      uint32_t byte = (start.ofs & (fs_info->fat.vol.bps - 1));;
892
893      ret = _fat_block_write(mt_entry, sec, byte + MSDOS_FILE_NAME_OFFSET, 1,
894                             &fchar);
895      if (ret < 0)
896        return -1;
897
898      if ((start.cln == end.cln) && (start.ofs == end.ofs))
899        break;
900
901      start.ofs += MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE;
902      if (start.ofs >= dir_block_size)
903      {
904        int rc;
905        if ((end.cln == fs_info->fat.vol.rdir_cl) &&
906            (fs_info->fat.vol.type & (FAT_FAT12 | FAT_FAT16)))
907          break;
908        rc = fat_get_fat_cluster(mt_entry, start.cln, &start.cln);
909        if ( rc != RC_OK )
910          return rc;
911        start.ofs = 0;
912      }
913    }
914
915    return  RC_OK;
916}
917
918/* msdos_dir_is_empty --
919 *     Check whether directory which correspondes to the fat-file descriptor is
920 *     empty.
921 *
922 * PARAMETERS:
923 *     mt_entry - mount table entry
924 *     fat_fd   - fat-file descriptor
925 *     ret_val  - placeholder for result
926 *
927 * RETURNS:
928 *     RC_OK on success, or -1 if error occured
929 *
930 */
931int
932msdos_dir_is_empty(
933    rtems_filesystem_mount_table_entry_t *mt_entry,
934    fat_file_fd_t                        *fat_fd,
935    bool                                 *ret_val
936    )
937{
938    ssize_t          ret = 0;
939    msdos_fs_info_t *fs_info = mt_entry->fs_info;
940    uint32_t         j = 0, i = 0;
941
942    /* dir is not empty */
943    *ret_val = false;
944
945    while ((ret = fat_file_read(mt_entry, fat_fd, j * fs_info->fat.vol.bps,
946                                  fs_info->fat.vol.bps,
947                                  fs_info->cl_buf)) != FAT_EOF)
948    {
949        if (ret < MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE)
950            return -1;
951
952        assert(ret == fs_info->fat.vol.bps);
953
954        /* have to look at the DIR_NAME as "raw" 8-bit data */
955        for (i = 0;
956             i < fs_info->fat.vol.bps;
957             i += MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE)
958        {
959            char* entry = (char*) fs_info->cl_buf + i;
960
961            /*
962             * If the entry is empty, a long file name entry, or '.' or '..'
963             * then consider it as empty.
964             *
965             * Just ignore long file name entries. They must have a short entry to
966             * be valid.
967             */
968            if (((*MSDOS_DIR_ENTRY_TYPE(entry)) ==
969                 MSDOS_THIS_DIR_ENTRY_EMPTY) ||
970                ((*MSDOS_DIR_ATTR(entry) & MSDOS_ATTR_LFN_MASK) ==
971                 MSDOS_ATTR_LFN) ||
972                (strncmp(MSDOS_DIR_NAME((entry)), MSDOS_DOT_NAME,
973                         MSDOS_SHORT_NAME_LEN) == 0) ||
974                (strncmp(MSDOS_DIR_NAME((entry)),
975                         MSDOS_DOTDOT_NAME,
976                         MSDOS_SHORT_NAME_LEN) == 0))
977                continue;
978
979            /*
980             * Nothing more to look at.
981             */
982            if ((*MSDOS_DIR_NAME(entry)) ==
983                MSDOS_THIS_DIR_ENTRY_AND_REST_EMPTY)
984            {
985                *ret_val = true;
986                return RC_OK;
987            }
988
989            /*
990             * Short file name entries mean not empty.
991             */
992            return RC_OK;
993        }
994        j++;
995    }
996    *ret_val = true;
997    return RC_OK;
998}
999
1000/* msdos_create_name_in_fat_file --
1001 *     This routine creates an entry in the fat file for the file name
1002 *     provided by the user. The directory entry passed is the short
1003 *     file name and is added as it. If the file name is long a long
1004 *     file name set of entries is added.
1005 *
1006 *     Scan the directory for the file and if not found add the new entry.
1007 *     When scanning remember the offset in the file where the directory
1008 *     entry can be added.
1009 *
1010 * PARAMETERS:
1011 *     mt_entry       - mount table entry
1012 *     fat_fd         - fat-file descriptor
1013 *     name           - NULL or name to find
1014 *     paux           - identify a node location on the disk -
1015 *                      number of cluster and offset inside the cluster
1016 *     name_dir_entry - node to create/placeholder for found node
1017 *
1018 * RETURNS:
1019 *     RC_OK on success, or error code if error occured (errno set
1020 *     appropriately)
1021 *
1022 */
1023#define MSDOS_FIND_PRINT 0
1024int msdos_find_name_in_fat_file(
1025    rtems_filesystem_mount_table_entry_t *mt_entry,
1026    fat_file_fd_t                        *fat_fd,
1027    bool                                  create_node,
1028    const char                           *name,
1029    int                                   name_len,
1030    msdos_name_type_t                     name_type,
1031    fat_dir_pos_t                        *dir_pos,
1032    char                                 *name_dir_entry
1033                                )
1034{
1035    ssize_t          ret = 0;
1036    msdos_fs_info_t *fs_info = mt_entry->fs_info;
1037    uint32_t         dir_offset = 0;
1038    uint32_t         dir_entry = 0;
1039    uint32_t         bts2rd = 0;
1040    fat_pos_t        lfn_start;
1041    bool             lfn_matched = false;
1042    uint8_t          lfn_checksum = 0;
1043    int              lfn_entries;
1044    int              lfn_entry = 0;
1045    uint32_t         empty_space_offset = 0;
1046    uint32_t         empty_space_entry = 0;
1047    uint32_t         empty_space_count = 0;
1048    bool             empty_space_found = false;
1049    uint32_t         entries_per_block;
1050    bool             read_cluster = false;
1051
1052    assert(name_len > 0);
1053
1054    fat_dir_pos_init(dir_pos);
1055
1056    lfn_start.cln = lfn_start.ofs = FAT_FILE_SHORT_NAME;
1057
1058    /*
1059     * Set the number of short entries needed to store the LFN. If the name
1060     * is short still check for possible long entries with the short name.
1061     *
1062     * In PR1491 we need to have a LFN for a short file name entry. To
1063     * test this make this test always fail, ie add "0 &&".
1064     */
1065    if (create_node && (name_type == MSDOS_NAME_SHORT))
1066      lfn_entries = 0;
1067    else
1068      lfn_entries =
1069        ((name_len - 1) + MSDOS_LFN_LEN_PER_ENTRY) / MSDOS_LFN_LEN_PER_ENTRY;
1070
1071    if (FAT_FD_OF_ROOT_DIR(fat_fd) &&
1072        (fs_info->fat.vol.type & (FAT_FAT12 | FAT_FAT16)))
1073        bts2rd = fat_fd->fat_file_size;
1074    else
1075        bts2rd = fs_info->fat.vol.bpc;
1076
1077    entries_per_block = bts2rd / MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE;
1078
1079#if MSDOS_FIND_PRINT
1080    printf ("MSFS:[1] nt:%d, cn:%i ebp:%li bts2rd:%li lfne:%d nl:%i n:%s\n",
1081            name_type, create_node, entries_per_block, bts2rd,
1082            lfn_entries, name_len, name);
1083#endif
1084    /*
1085     * Scan the directory seeing if the file is present. While
1086     * doing this see if a suitable location can be found to
1087     * create the entry if the name is not found.
1088     */
1089    while ((ret = fat_file_read(mt_entry, fat_fd, (dir_offset * bts2rd),
1090                                bts2rd, fs_info->cl_buf)) != FAT_EOF)
1091    {
1092        bool remainder_empty = false;
1093#if MSDOS_FIND_PRINT
1094        printf ("MSFS:[2] dir_offset:%li\n", dir_offset);
1095#endif
1096
1097        if (ret < MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE)
1098            rtems_set_errno_and_return_minus_one(EIO);
1099
1100        assert(ret == bts2rd);
1101
1102        /* have to look at the DIR_NAME as "raw" 8-bit data */
1103        for (dir_entry = 0;
1104             dir_entry < bts2rd;
1105             dir_entry += MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE)
1106        {
1107            char* entry = (char*) fs_info->cl_buf + dir_entry;
1108
1109            /*
1110             * See if the entry is empty or the remainder of the directory is
1111             * empty ? Localise to make the code read better.
1112             */
1113            bool entry_empty = (*MSDOS_DIR_ENTRY_TYPE(entry) ==
1114                                MSDOS_THIS_DIR_ENTRY_EMPTY);
1115            remainder_empty = (*MSDOS_DIR_ENTRY_TYPE(entry) ==
1116                               MSDOS_THIS_DIR_ENTRY_AND_REST_EMPTY);
1117#if MSDOS_FIND_PRINT
1118            printf ("MSFS:[3] re:%i ee:%i do:%li de:%li(%ld)\n",
1119                    remainder_empty, entry_empty, dir_offset,
1120                    dir_entry, (dir_entry / MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE));
1121#endif
1122            /*
1123             * Remember where the we are, ie the start, so we can come back
1124             * to here and write the long file name if this is the start of
1125             * a series of empty entries. If empty_space_count is 0 then
1126             * we are currently not inside an empty series of entries. It
1127             * is a count of empty entries.
1128             */
1129            if (empty_space_count == 0)
1130            {
1131                empty_space_entry = dir_entry;
1132                empty_space_offset = dir_offset;
1133            }
1134
1135            if (remainder_empty)
1136            {
1137#if MSDOS_FIND_PRINT
1138                printf ("MSFS:[3.1] cn:%i esf:%i\n", create_node, empty_space_found);
1139#endif
1140                /*
1141                 * If just looking and there is no more entries in the
1142                 * directory - return name-not-found
1143                 */
1144                if (!create_node)
1145                    return MSDOS_NAME_NOT_FOUND_ERR;
1146
1147                /*
1148                 * Lets go and write the directory entries. If we have not found
1149                 * any available space add the remaining number of entries to any that
1150                 * we may have already found that are just before this entry. If more
1151                 * are needed FAT_EOF is returned by the read and we extend the file.
1152                 */
1153                if (!empty_space_found)
1154                {
1155                  empty_space_count +=
1156                    entries_per_block - (dir_entry / MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE);
1157                  empty_space_found = true;
1158#if MSDOS_FIND_PRINT
1159                  printf ("MSFS:[3.2] esf:%i esc%i\n", empty_space_found, empty_space_count);
1160#endif
1161                }
1162                break;
1163            }
1164            else if (entry_empty)
1165            {
1166                if (create_node)
1167                {
1168                  /*
1169                   * Remainder is not empty so is this entry empty ?
1170                   */
1171                  empty_space_count++;
1172
1173                  if (empty_space_count == (lfn_entries + 1))
1174                    empty_space_found = true;
1175                }
1176#if MSDOS_FIND_PRINT
1177                printf ("MSFS:[4.1] esc:%li esf:%i\n",
1178                        empty_space_count, empty_space_found);
1179#endif
1180            }
1181            else
1182            {
1183                /*
1184                 * A valid entry so handle it.
1185                 *
1186                 * If empty space has not been found we need to start the
1187                 * count again.
1188                 */
1189                if (create_node && !empty_space_found)
1190                {
1191                    empty_space_entry = 0;
1192                    empty_space_count = 0;
1193                }
1194
1195                /*
1196                 * Check the attribute to see if the entry is for a long
1197                 * file name.
1198                 */
1199                if ((*MSDOS_DIR_ATTR(entry) & MSDOS_ATTR_LFN_MASK) ==
1200                    MSDOS_ATTR_LFN)
1201                {
1202                    char* p;
1203                    int   o;
1204                    int   i;
1205#if MSDOS_FIND_PRINT
1206                    printf ("MSFS:[4.2] lfn:%c entry:%i checksum:%i\n",
1207                            lfn_start.cln == FAT_FILE_SHORT_NAME ? 'f' : 't',
1208                            *MSDOS_DIR_ENTRY_TYPE(entry) & MSDOS_LAST_LONG_ENTRY_MASK,
1209                            *MSDOS_DIR_LFN_CHECKSUM(entry));
1210#endif
1211                    /*
1212                     * If we are not already processing a LFN see if this is
1213                     * the first entry of a LFN ?
1214                     */
1215                    if (lfn_start.cln == FAT_FILE_SHORT_NAME)
1216                    {
1217                        lfn_matched = false;
1218
1219                        /*
1220                         * The first entry must have the last long entry
1221                         * flag set.
1222                         */
1223                        if ((*MSDOS_DIR_ENTRY_TYPE(entry) &
1224                             MSDOS_LAST_LONG_ENTRY) == 0)
1225                            continue;
1226
1227                        /*
1228                         * Does the number of entries in the LFN directory
1229                         * entry match the number we expect for this
1230                         * file name. Note we do not know the number of
1231                         * characters in the entry so this is check further
1232                         * on when the characters are checked.
1233                         */
1234                        if (lfn_entries != (*MSDOS_DIR_ENTRY_TYPE(entry) &
1235                                            MSDOS_LAST_LONG_ENTRY_MASK))
1236                            continue;
1237
1238                        /*
1239                         * Get the checksum of the short entry.
1240                         */
1241                        lfn_start.cln = dir_offset;
1242                        lfn_start.ofs = dir_entry;
1243                        lfn_entry = lfn_entries;
1244                        lfn_checksum = *MSDOS_DIR_LFN_CHECKSUM(entry);
1245
1246#if MSDOS_FIND_PRINT
1247                        printf ("MSFS:[4.3] lfn_checksum:%i\n",
1248                                *MSDOS_DIR_LFN_CHECKSUM(entry));
1249#endif
1250                    }
1251
1252                    /*
1253                     * If the entry number or the check sum do not match
1254                     * forget this series of long directory entries. These
1255                     * could be orphaned entries depending on the history
1256                     * of the disk.
1257                     */
1258                    if ((lfn_entry != (*MSDOS_DIR_ENTRY_TYPE(entry) &
1259                                       MSDOS_LAST_LONG_ENTRY_MASK)) ||
1260                        (lfn_checksum != *MSDOS_DIR_LFN_CHECKSUM(entry)))
1261                    {
1262#if MSDOS_FIND_PRINT
1263                        printf ("MSFS:[4.4] no match\n");
1264#endif
1265                        lfn_start.cln = FAT_FILE_SHORT_NAME;
1266                        continue;
1267                    }
1268
1269                    lfn_entry--;
1270                    o = lfn_entry * MSDOS_LFN_LEN_PER_ENTRY;
1271                    p = entry + 1;
1272
1273#if MSDOS_FIND_PRINT
1274                    printf ("MSFS:[5] lfne:%i\n", lfn_entry);
1275#endif
1276                    for (i = 0; i < MSDOS_LFN_LEN_PER_ENTRY; i++)
1277                    {
1278#if MSDOS_FIND_PRINT > 1
1279                        printf ("MSFS:[6] o:%i i:%i *p:%c(%02x) name[o + i]:%c(%02x)\n",
1280                                o, i, *p, *p, name[o + i], name[o + i]);
1281#endif
1282                        if (*p == '\0')
1283                        {
1284                            /*
1285                             * If this is the first entry, ie the last part of the
1286                             * long file name and the length does not match then
1287                             * the file names do not match.
1288                             */
1289                            if (((lfn_entry + 1) == lfn_entries) &&
1290                                ((o + i) != name_len))
1291                                lfn_start.cln = FAT_FILE_SHORT_NAME;
1292                            break;
1293                        }
1294
1295                        if (((o + i) >= name_len) || (*p != name[o + i]))
1296                        {
1297                            lfn_start.cln = FAT_FILE_SHORT_NAME;
1298                            break;
1299                        }
1300
1301                        switch (i)
1302                        {
1303                            case 4:
1304                                p += 5;
1305                                break;
1306                            case 10:
1307                                p += 4;
1308                                break;
1309                            default:
1310                                p += 2;
1311                                break;
1312                        }
1313                    }
1314
1315                    lfn_matched = ((lfn_entry == 0) &&
1316                                   (lfn_start.cln != FAT_FILE_SHORT_NAME));
1317
1318#if MSDOS_FIND_PRINT
1319                    printf ("MSFS:[8.1] lfn_matched:%i\n", lfn_matched);
1320#endif
1321                }
1322                else
1323                {
1324#if MSDOS_FIND_PRINT
1325                    printf ("MSFS:[9.1] SFN entry, lfn_matched:%i\n", lfn_matched);
1326#endif
1327                    /*
1328                     * SFN entry found.
1329                     *
1330                     * If a LFN has been found and it matched check the
1331                     * entries have all been found and the checksum is
1332                     * correct. If this is the case return the short file
1333                     * name entry.
1334                     */
1335                    if (lfn_matched)
1336                    {
1337                        uint8_t  cs = 0;
1338                        uint8_t* p = (uint8_t*) MSDOS_DIR_NAME(entry);
1339                        int      i;
1340
1341                        for (i = 0; i < MSDOS_SHORT_NAME_LEN; i++, p++)
1342                            cs = ((cs & 1) ? 0x80 : 0) + (cs >> 1) + *p;
1343
1344                        if (lfn_entry || (lfn_checksum != cs))
1345                            lfn_matched = false;
1346#if MSDOS_FIND_PRINT
1347                        printf ("MSFS:[9.2] checksum, lfn_matched:%i, lfn_entry:%i, lfn_checksum:%02x/%02x\n",
1348                                lfn_matched, lfn_entry, lfn_checksum, cs);
1349#endif
1350                    }
1351
1352                    /*
1353                     * If the long file names matched or the file name is
1354                     * short and they match then we have the entry. We will not
1355                     * match a long file name against a short file name because
1356                     * a long file name that generates a matching short file
1357                     * name is not a long file name.
1358                     */
1359                    if (lfn_matched ||
1360                        ((name_type == MSDOS_NAME_SHORT) &&
1361                         (lfn_start.cln == FAT_FILE_SHORT_NAME) &&
1362                         (memcmp(MSDOS_DIR_NAME(entry),
1363                                 MSDOS_DIR_NAME(name_dir_entry),
1364                                 MSDOS_SHORT_NAME_LEN) == 0)))
1365                    {
1366#if MSDOS_FIND_PRINT
1367                        printf ("MSFS:[9.3] SNF found\n");
1368#endif
1369                        /*
1370                         * We get the entry we looked for - fill the position
1371                         * structure and the 32 bytes of the short entry
1372                         */
1373                        int rc = fat_file_ioctl(mt_entry, fat_fd, F_CLU_NUM,
1374                                                dir_offset * bts2rd,
1375                                                &dir_pos->sname.cln);
1376                        if (rc != RC_OK)
1377                            return rc;
1378
1379                        dir_pos->sname.ofs = dir_entry;
1380
1381                        if (lfn_start.cln != FAT_FILE_SHORT_NAME)
1382                        {
1383                          rc = fat_file_ioctl(mt_entry, fat_fd, F_CLU_NUM,
1384                                              lfn_start.cln * bts2rd,
1385                                              &lfn_start.cln);
1386                          if (rc != RC_OK)
1387                            return rc;
1388                        }
1389
1390                        dir_pos->lname.cln = lfn_start.cln;
1391                        dir_pos->lname.ofs = lfn_start.ofs;
1392
1393                        memcpy(name_dir_entry, entry,
1394                               MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE);
1395                        return RC_OK;
1396                    }
1397
1398                    lfn_start.cln = FAT_FILE_SHORT_NAME;
1399                    lfn_matched = false;
1400                }
1401            }
1402        }
1403
1404        if (remainder_empty)
1405            break;
1406
1407        dir_offset++;
1408    }
1409
1410    /*
1411     * If we are not to create the entry return a not found error.
1412     */
1413    if (!create_node)
1414      return MSDOS_NAME_NOT_FOUND_ERR;
1415
1416#if MSDOS_FIND_PRINT
1417    printf ("MSFS:[8.1] WRITE do:%ld esc:%ld eso:%ld ese:%ld\n",
1418            dir_offset, empty_space_count, empty_space_offset, empty_space_entry);
1419#endif
1420
1421    /*
1422     * If a long file name calculate the checksum of the short file name
1423     * data to place in each long file name entry. First set the short
1424     * file name to the slot of the SFN entry. This will mean no clashes
1425     * in this directory.
1426     */
1427    lfn_checksum = 0;
1428    if (name_type == MSDOS_NAME_LONG)
1429    {
1430        int      slot = (((empty_space_offset * bts2rd) + empty_space_entry) /
1431                         MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE) + lfn_entries + 1;
1432        msdos_short_name_hex(MSDOS_DIR_NAME(name_dir_entry), slot);
1433    }
1434
1435    if (lfn_entries)
1436    {
1437        uint8_t* p = (uint8_t*) MSDOS_DIR_NAME(name_dir_entry);
1438        int      i;
1439        for (i = 0; i < 11; i++, p++)
1440            lfn_checksum =
1441                ((lfn_checksum & 1) ? 0x80 : 0) + (lfn_checksum >> 1) + *p;
1442    }
1443
1444    /*
1445     * If there is no space available then extend the file. The
1446     * empty_space_count is a count of empty entries in the currently
1447     * read cluster so if 0 there is no space. Note, dir_offset will
1448     * be at the next cluster so we can just make empty_space_offset
1449     * that value.
1450     */
1451    if (empty_space_count == 0)
1452    {
1453        read_cluster = true;
1454        empty_space_offset = dir_offset;
1455        empty_space_entry = 0;
1456    }
1457
1458    /*
1459     * Have we read past the empty block ? If so go back and read it again.
1460     */
1461    if (dir_offset != empty_space_offset)
1462        read_cluster = true;
1463
1464    /*
1465     * Handle the entry writes.
1466     */
1467    lfn_start.cln = lfn_start.ofs = FAT_FILE_SHORT_NAME;
1468    lfn_entry = 0;
1469
1470#if MSDOS_FIND_PRINT
1471    printf ("MSFS:[9] read_cluster:%d eso:%ld ese:%ld\n",
1472            read_cluster, empty_space_offset, empty_space_entry);
1473#endif
1474
1475    /*
1476     * The one more is the short entry.
1477     */
1478    while (lfn_entry < (lfn_entries + 1))
1479    {
1480        int length = 0;
1481
1482        if (read_cluster)
1483        {
1484          uint32_t new_length;
1485#if MSDOS_FIND_PRINT
1486          printf ("MSFS:[9.1] eso:%li\n", empty_space_offset);
1487#endif
1488          ret = fat_file_read(mt_entry, fat_fd,
1489                              (empty_space_offset * bts2rd), bts2rd,
1490                              fs_info->cl_buf);
1491
1492          if (ret != bts2rd)
1493          {
1494            if (ret != FAT_EOF)
1495              rtems_set_errno_and_return_minus_one(EIO);
1496
1497#if MSDOS_FIND_PRINT
1498            printf ("MSFS:[9.2] extending file:%li\n", empty_space_offset);
1499#endif
1500            ret = fat_file_extend (mt_entry, fat_fd, empty_space_offset * bts2rd,
1501                                   &new_length);
1502
1503            if (ret != RC_OK)
1504              return ret;
1505
1506#if MSDOS_FIND_PRINT
1507            printf ("MSFS:[9.3] extended: %d <-> %d\n", new_length, empty_space_offset * bts2rd);
1508#endif
1509            if (new_length != (empty_space_offset * bts2rd))
1510              rtems_set_errno_and_return_minus_one(EIO);
1511
1512            memset(fs_info->cl_buf, 0, bts2rd);
1513
1514            ret = fat_file_write(mt_entry, fat_fd,
1515                                 empty_space_offset * bts2rd,
1516                                 bts2rd, fs_info->cl_buf);
1517#if MSDOS_FIND_PRINT
1518            printf ("MSFS:[9.4] clear write: %d\n", ret);
1519#endif
1520            if (ret == -1)
1521              return ret;
1522            else if (ret != bts2rd)
1523              rtems_set_errno_and_return_minus_one(EIO);
1524          }
1525        }
1526
1527#if MSDOS_FIND_PRINT
1528        printf ("MSFS:[10] eso:%li\n", empty_space_offset);
1529#endif
1530
1531        for (dir_entry = empty_space_entry;
1532             dir_entry < bts2rd;
1533             dir_entry += MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE)
1534        {
1535            char*       entry = (char*) fs_info->cl_buf + dir_entry;
1536            char*       p;
1537            const char* n;
1538            int         i;
1539            char        fill = 0;
1540
1541            length += MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE;
1542            lfn_entry++;
1543
1544#if MSDOS_FIND_PRINT
1545            printf ("MSFS:[10] de:%li(%li) length:%i lfn_entry:%i\n",
1546                    dir_entry, (dir_entry / MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE),
1547                    length, lfn_entry);
1548#endif
1549            /*
1550             * Time to write the short file name entry.
1551             */
1552            if (lfn_entry == (lfn_entries + 1))
1553            {
1554                /* get current cluster number */
1555                int rc = fat_file_ioctl(mt_entry, fat_fd, F_CLU_NUM,
1556                                        empty_space_offset * bts2rd,
1557                                        &dir_pos->sname.cln);
1558                if (rc != RC_OK)
1559                  return rc;
1560
1561                dir_pos->sname.ofs = dir_entry;
1562
1563                if (lfn_start.cln != FAT_FILE_SHORT_NAME)
1564                {
1565                  rc = fat_file_ioctl(mt_entry, fat_fd, F_CLU_NUM,
1566                                      lfn_start.cln * bts2rd,
1567                                      &lfn_start.cln);
1568                  if (rc != RC_OK)
1569                    return rc;
1570                }
1571
1572                dir_pos->lname.cln = lfn_start.cln;
1573                dir_pos->lname.ofs = lfn_start.ofs;
1574
1575                /* write new node entry */
1576                memcpy (entry, (uint8_t *) name_dir_entry,
1577                        MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE);
1578                break;
1579            }
1580
1581            /*
1582             * This is a long file name and we need to write
1583             * a long file name entry. See if this is the
1584             * first entry written and if so remember the
1585             * the location of the long file name.
1586             */
1587            if (lfn_start.cln == FAT_FILE_SHORT_NAME)
1588            {
1589              lfn_start.cln = empty_space_offset;
1590              lfn_start.ofs = dir_entry;
1591            }
1592
1593            /*
1594             * Clear the entry before loading the data.
1595             */
1596            memset (entry, 0, MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE);
1597
1598            *MSDOS_DIR_LFN_CHECKSUM(entry) = lfn_checksum;
1599
1600            p = entry + 1;
1601            n = name + (lfn_entries - lfn_entry) * MSDOS_LFN_LEN_PER_ENTRY;
1602
1603            for (i = 0; i < MSDOS_LFN_LEN_PER_ENTRY; i++)
1604            {
1605                if (*n != 0)
1606                {
1607                    *p = *n;
1608                    n++;
1609                }
1610                else
1611                {
1612                    p [0] = fill;
1613                    p [1] = fill;
1614                    fill = 0xff;
1615                }
1616
1617                switch (i)
1618                {
1619                    case 4:
1620                        p += 5;
1621                        break;
1622                    case 10:
1623                        p += 4;
1624                        break;
1625                    default:
1626                        p += 2;
1627                        break;
1628                }
1629            }
1630
1631            *MSDOS_DIR_ENTRY_TYPE(entry) = (lfn_entries - lfn_entry) + 1;
1632            if (lfn_entry == 1)
1633                *MSDOS_DIR_ENTRY_TYPE(entry) |= MSDOS_LAST_LONG_ENTRY;
1634            *MSDOS_DIR_ATTR(entry) |= MSDOS_ATTR_LFN;
1635        }
1636
1637        ret = fat_file_write(mt_entry, fat_fd,
1638                             (empty_space_offset * bts2rd) + empty_space_entry,
1639                             length, fs_info->cl_buf + empty_space_entry);
1640        if (ret == -1)
1641            return ret;
1642        else if (ret != length)
1643            rtems_set_errno_and_return_minus_one(EIO);
1644
1645        empty_space_offset++;
1646        empty_space_entry = 0;
1647        read_cluster = true;
1648    }
1649
1650    return 0;
1651}
1652
1653/* msdos_find_node_by_cluster_num_in_fat_file --
1654 *     Find node with specified number of cluster in fat-file.
1655 *
1656 * Note, not updated in the LFN change because it is only used
1657 *       for . and .. entries and these are always short.
1658 *
1659 * PARAMETERS:
1660 *     mt_entry  - mount table entry
1661 *     fat_fd    - fat-file descriptor
1662 *     cl4find   - number of cluster to find
1663 *     paux      - identify a node location on the disk -
1664 *                 cluster num and offset inside the cluster
1665 *     dir_entry - placeholder for found node
1666 *
1667 * RETURNS:
1668 *     RC_OK on success, or error code if error occured
1669 *
1670 */
1671int msdos_find_node_by_cluster_num_in_fat_file(
1672    rtems_filesystem_mount_table_entry_t *mt_entry,
1673    fat_file_fd_t                        *fat_fd,
1674    uint32_t                              cl4find,
1675    fat_dir_pos_t                        *dir_pos,
1676    char                                 *dir_entry
1677    )
1678{
1679    int              rc = RC_OK;
1680    ssize_t          ret = 0;
1681    msdos_fs_info_t *fs_info = mt_entry->fs_info;
1682    uint32_t         bts2rd = 0;
1683    uint32_t         i = 0, j = 0;
1684
1685    if (FAT_FD_OF_ROOT_DIR(fat_fd) &&
1686       (fs_info->fat.vol.type & (FAT_FAT12 | FAT_FAT16)))
1687        bts2rd = fat_fd->fat_file_size;
1688    else
1689        bts2rd = fs_info->fat.vol.bpc;
1690
1691    while ((ret = fat_file_read(mt_entry, fat_fd, j * bts2rd, bts2rd,
1692                                  fs_info->cl_buf)) != FAT_EOF)
1693    {
1694        if ( ret < MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE )
1695            rtems_set_errno_and_return_minus_one( EIO );
1696
1697        assert(ret == bts2rd);
1698
1699        for (i = 0; i < bts2rd; i += MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE)
1700        {
1701            char* entry = (char*) fs_info->cl_buf + i;
1702
1703            /* if this and all rest entries are empty - return not-found */
1704            if ((*MSDOS_DIR_ENTRY_TYPE(entry)) ==
1705                MSDOS_THIS_DIR_ENTRY_AND_REST_EMPTY)
1706                return MSDOS_NAME_NOT_FOUND_ERR;
1707
1708            /* if this entry is empty - skip it */
1709            if ((*MSDOS_DIR_ENTRY_TYPE(entry)) ==
1710                MSDOS_THIS_DIR_ENTRY_EMPTY)
1711                continue;
1712
1713            /* if get a non-empty entry - compare clusters num */
1714            if (MSDOS_EXTRACT_CLUSTER_NUM(entry) == cl4find)
1715            {
1716                /* on success fill aux structure and copy all 32 bytes */
1717                rc = fat_file_ioctl(mt_entry, fat_fd, F_CLU_NUM, j * bts2rd,
1718                                    &dir_pos->sname.cln);
1719                if (rc != RC_OK)
1720                    return rc;
1721
1722                dir_pos->sname.ofs = i;
1723                dir_pos->lname.cln = FAT_FILE_SHORT_NAME;
1724                dir_pos->lname.ofs = FAT_FILE_SHORT_NAME;
1725
1726                memcpy(dir_entry, entry,
1727                       MSDOS_DIRECTORY_ENTRY_STRUCT_SIZE);
1728                return RC_OK;
1729            }
1730        }
1731        j++;
1732    }
1733    return MSDOS_NAME_NOT_FOUND_ERR;
1734}
Note: See TracBrowser for help on using the repository browser.