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

4.11
Last change on this file since 721fe34 was 721fe34, checked in by Joel Sherrill <joel.sherrill@…>, on May 31, 2012 at 8:34:36 PM

Fix C files which had two semi-colons at EOL

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