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

4.115
Last change on this file since 3d0c96c7 was 3d0c96c7, checked in by Sebastian Huber <sebastian.huber@…>, on 05/07/12 at 14:31:15

Filesystem: PR1893: Fix write and truncate handler

Space that grows due to truncate or write offsets beyond the current
file size must be zero filled.

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