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

4.115
Last change on this file since 8485c7f was 8485c7f, checked in by Ralf Corsepius <ralf.corsepius@…>, on 02/23/11 at 13:57:02

2011-02-23 Ralf Corsépius <ralf.corsepius@…>

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