1 /***************************************************************************
2  * Copyright (c) 2024 Microsoft Corporation
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the MIT License which is available at
6  * https://opensource.org/licenses/MIT.
7  *
8  * SPDX-License-Identifier: MIT
9  **************************************************************************/
10 
11 
12 /**************************************************************************/
13 /**************************************************************************/
14 /**                                                                       */
15 /** FileX Component                                                       */
16 /**                                                                       */
17 /**   Directory                                                           */
18 /**                                                                       */
19 /**************************************************************************/
20 /**************************************************************************/
21 
22 #define FX_SOURCE_CODE
23 
24 
25 /* Include necessary system files.  */
26 
27 #include "fx_api.h"
28 #include "fx_system.h"
29 #include "fx_directory.h"
30 #include "fx_utility.h"
31 #ifdef FX_ENABLE_FAULT_TOLERANT
32 #include "fx_fault_tolerant.h"
33 #endif /* FX_ENABLE_FAULT_TOLERANT */
34 
35 
36 /**************************************************************************/
37 /*                                                                        */
38 /*  FUNCTION                                               RELEASE        */
39 /*                                                                        */
40 /*    _fx_directory_entry_write                           PORTABLE C      */
41 /*                                                           6.1.5        */
42 /*  AUTHOR                                                                */
43 /*                                                                        */
44 /*    William E. Lamie, Microsoft Corporation                             */
45 /*                                                                        */
46 /*  DESCRIPTION                                                           */
47 /*                                                                        */
48 /*    This function writes the supplied directory entry to the specified  */
49 /*    logical sector and offset.                                          */
50 /*                                                                        */
51 /*  INPUT                                                                 */
52 /*                                                                        */
53 /*    media_ptr                             Media control block pointer   */
54 /*    entry_ptr                             Pointer to directory entry    */
55 /*                                                                        */
56 /*  OUTPUT                                                                */
57 /*                                                                        */
58 /*    return status                                                       */
59 /*                                                                        */
60 /*  CALLS                                                                 */
61 /*                                                                        */
62 /*    _fx_utility_FAT_entry_read            Read a new FAT entry          */
63 /*    _fx_utility_logical_sector_read       Read directory sector         */
64 /*    _fx_utility_logical_sector_write      Write directory sector        */
65 /*    _fx_utility_16_unsigned_write         Write a UINT from memory      */
66 /*    _fx_utility_32_unsigned_write         Write a ULONG from memory     */
67 /*    _fx_fault_tolerant_add_dir_log        Add directory redo log        */
68 /*                                                                        */
69 /*  CALLED BY                                                             */
70 /*                                                                        */
71 /*    FileX System Functions                                              */
72 /*                                                                        */
73 /*  RELEASE HISTORY                                                       */
74 /*                                                                        */
75 /*    DATE              NAME                      DESCRIPTION             */
76 /*                                                                        */
77 /*  05-19-2020     William E. Lamie         Initial Version 6.0           */
78 /*  09-30-2020     William E. Lamie         Modified comment(s),          */
79 /*                                            resulting in version 6.1    */
80 /*  03-02-2021     William E. Lamie         Modified comment(s),          */
81 /*                                            resulting in version 6.1.5  */
82 /*                                                                        */
83 /**************************************************************************/
_fx_directory_entry_write(FX_MEDIA * media_ptr,FX_DIR_ENTRY * entry_ptr)84 UINT  _fx_directory_entry_write(FX_MEDIA *media_ptr, FX_DIR_ENTRY *entry_ptr)
85 {
86 
87 UCHAR *work_ptr, *sector_base_ptr;
88 UINT   status, temp, entry, delete_flag;
89 UINT   i, j, k, l, card, len, dotfound, dotpos, match;
90 UCHAR  checksum, eof_marker;
91 CHAR   alpha;
92 CHAR   shortname[FX_DIR_NAME_SIZE + FX_DIR_EXT_SIZE + 1];
93 ULONG  logical_sector, relative_sector;
94 ULONG  byte_offset;
95 ULONG  cluster, next_cluster;
96 
97 
98 #ifdef FX_ENABLE_FAULT_TOLERANT
99 UCHAR *changed_ptr;
100 UINT   changed_size;
101 ULONG  changed_offset;
102 #endif /* FX_ENABLE_FAULT_TOLERANT */
103 
104 
105 #ifndef FX_MEDIA_STATISTICS_DISABLE
106 
107     /* Increment the number of directory entry write requests.  */
108     media_ptr -> fx_media_directory_entry_writes++;
109 #endif
110 
111     /* Extended port-specific processing macro, which is by default defined to white space.  */
112     FX_DIRECTORY_ENTRY_WRITE_EXTENSION
113 
114     /* If trace is enabled, insert this event into the trace buffer.  */
115     FX_TRACE_IN_LINE_INSERT(FX_TRACE_INTERNAL_DIR_ENTRY_WRITE, media_ptr, 0, 0, 0, FX_TRACE_INTERNAL_EVENTS, 0, 0)
116 
117     /* Determine if this is entry is being deleted.  */
118     if (((UCHAR)entry_ptr -> fx_dir_entry_name[0] == (UCHAR)FX_DIR_ENTRY_FREE) &&
119         ((UCHAR)entry_ptr -> fx_dir_entry_short_name[0] == (UCHAR)FX_DIR_ENTRY_FREE))
120     {
121 
122         /* Yes, this is a request to delete the entry. Set the flag to remember this.  */
123         delete_flag =  FX_TRUE;
124 
125         /* Null the short file name.  */
126         entry_ptr -> fx_dir_entry_short_name[0] =  0;
127     }
128     else
129     {
130 
131         /* Not a deleted entry. Set the flag to false.  */
132         delete_flag =  FX_FALSE;
133     }
134 
135     /* Pickup the byte offset of the entry.  */
136     byte_offset = entry_ptr -> fx_dir_entry_byte_offset;
137 
138     /* Pickup the logical sector of the entry.  */
139     logical_sector = (ULONG)entry_ptr -> fx_dir_entry_log_sector;
140 
141     /* Figure out where what cluster we are in.  */
142     if (logical_sector >= (ULONG)(media_ptr -> fx_media_data_sector_start))
143     {
144 
145         /* Calculate the cluster that this logical sector is in.  */
146         cluster =  (logical_sector - media_ptr -> fx_media_data_sector_start) / (media_ptr -> fx_media_sectors_per_cluster) + FX_FAT_ENTRY_START;
147 
148         /* Calculate the relative cluster.  */
149         relative_sector =  logical_sector -  (((ULONG)media_ptr -> fx_media_data_sector_start) +
150                                               (((ULONG)cluster - FX_FAT_ENTRY_START) *
151                                                ((ULONG)media_ptr -> fx_media_sectors_per_cluster)));
152     }
153     else
154     {
155 
156         /* Clear the cluster and the relative sector.  */
157         cluster =  0;
158         relative_sector =  0;
159     }
160 
161     /* Read the logical directory sector.  */
162     status =  _fx_utility_logical_sector_read(media_ptr, (ULONG64) entry_ptr -> fx_dir_entry_log_sector,
163                                               media_ptr -> fx_media_memory_buffer, ((ULONG) 1), FX_DIRECTORY_SECTOR);
164 
165     /* Determine if an error occurred.  */
166     if (status != FX_SUCCESS)
167     {
168 
169         /* Return the error status.  */
170         return(status);
171     }
172 
173     /* Setup a pointer into the buffer.  */
174     sector_base_ptr = (UCHAR *)media_ptr -> fx_media_memory_buffer;
175     work_ptr =  sector_base_ptr + (UINT)entry_ptr -> fx_dir_entry_byte_offset;
176 
177 #ifdef FX_ENABLE_FAULT_TOLERANT
178     /* Initialize data for fault tolerant. */
179     changed_ptr = work_ptr;
180     changed_size = 0;
181     changed_offset = entry_ptr -> fx_dir_entry_byte_offset;
182 #endif /* FX_ENABLE_FAULT_TOLERANT */
183 
184     /* Determine if a long file name is present.  */
185     if (entry_ptr -> fx_dir_entry_long_name_present)
186     {
187 
188         /* Yes, long name is present - prepare short name and write out this name.  */
189         for (len = 0, i = 0, dotpos = 0, dotfound = 0; entry_ptr -> fx_dir_entry_name[len]; len++)
190         {
191 
192             /* Check for a dot.  */
193             if (entry_ptr -> fx_dir_entry_name[len] == '.')
194             {
195 
196                 /* Check for leading dot. */
197                 if (len == 0)
198                 {
199                     /* Yes, this is a leading dot. */
200                     continue;
201                 }
202 
203                 /* Yes, a dot is present.  From this position the extension will
204                    be written.  */
205                 dotfound = i;
206                 dotpos   = len + 1;
207                 continue;
208             }
209 
210             /* Check for non-space and within the short file name length.  */
211             if ((entry_ptr -> fx_dir_entry_name[len] != ' ') && (i < 8))
212             {
213 
214                 /* Copy characters into the short file name area.  */
215                 shortname[i] = entry_ptr -> fx_dir_entry_name[len];
216                 i++;
217             }
218         }
219 
220         /* Fill remaining short file name with spaces.  */
221         for (j = i; j < FX_DIR_NAME_SIZE + FX_DIR_EXT_SIZE; j++)
222         {
223             shortname[j] =  ' ';
224         }
225 
226         /* Determine if a dot was encountered.  */
227         if (dotpos)
228         {
229 
230             /* Process relative to the dot position.  */
231             if (entry_ptr -> fx_dir_entry_name[dotpos])
232             {
233                 shortname[8] = entry_ptr -> fx_dir_entry_name[dotpos++];
234             }
235             if (entry_ptr -> fx_dir_entry_name[dotpos])
236             {
237                 shortname[9] = entry_ptr -> fx_dir_entry_name[dotpos++];
238             }
239             if (entry_ptr -> fx_dir_entry_name[dotpos])
240             {
241                 shortname[10] = entry_ptr -> fx_dir_entry_name[dotpos++];
242             }
243 
244             /* Determine if additional spaces are needed.  */
245             i = dotfound;
246 
247             for (; dotfound <= 7; dotfound++)
248             {
249                 /* Add space...  */
250                 shortname[dotfound] = ' ';
251             }
252         }
253 
254         /* Each entry contains 13 unicode entries.  Calculate the remainder.  */
255         if (len % 13 == 0)
256         {
257             card =  len / 13;
258         }
259         else
260         {
261             card =  len / 13 + 1;
262         }
263 
264         /* Default the name match to true.  */
265         match =  FX_TRUE;
266 
267         /* Loop through the newly derived short name and the original name and look
268            for a non-matching character.  */
269         l =  0;
270         k =  0;
271         while (k < FX_DIR_NAME_SIZE + FX_DIR_EXT_SIZE)
272         {
273 
274             /* Determine if a space is detected in the short name. If so,
275                advance to the extension index.  */
276             if (shortname[k] == ' ')
277             {
278 
279                 /* The first pad space was detected. First, check for a name
280                    without an extension.  */
281                 if (entry_ptr -> fx_dir_entry_name[l] == FX_NULL)
282                 {
283 
284                     /* All is okay, get out of the loop!  */
285                     break;
286                 }
287 
288                 /* Now check for a period in the long name... if not, there is a non-match!  */
289                 if (entry_ptr -> fx_dir_entry_name[l] != '.')
290                 {
291 
292                     /* Set the match flag to false and exit the loop.  */
293                     match =  FX_FALSE;
294                     break;
295                 }
296 
297                 /* Otherwise move short file name index to the extension area and
298                    increment the long file name index.  */
299                 k =  8;
300                 l++;
301 
302                 /* Restart the loop at the top.  */
303                 continue;
304             }
305 
306             /* Check for the dot for the 8.3 match... it is no longer in the
307                shortname but possibly still present in the long name.  */
308             if ((k == 8) && (entry_ptr -> fx_dir_entry_name[l] == '.'))
309             {
310 
311                 /* Yes, handle the implicit dot in the shortname by
312                    positioning past it in the long name.  */
313                 l++;
314             }
315 
316             /* Do the names match?  */
317             if (shortname[k] != entry_ptr -> fx_dir_entry_name[l])
318             {
319 
320                 /* No, the names do not match, set the match flag to false and
321                    exit the loop.  */
322                 match =  FX_FALSE;
323                 break;
324             }
325 
326             /* Move the indices forward.  */
327             k++;
328             l++;
329         }
330 
331         /* Check if there is a dot in the name, but no extension in the short name.  In this case,
332            we should create a mangled short name.  */
333         if ((dotpos) && (shortname[8] == ' '))
334         {
335 
336             /* Something left.. the names do not match!  */
337             match =  FX_FALSE;
338         }
339 
340         /* One final check to make sure there is nothing left on the long file name.  */
341         if (entry_ptr -> fx_dir_entry_name[l])
342         {
343 
344             /* Something left.. the names do not match!  */
345             match =  FX_FALSE;
346         }
347 
348         /* Determine if the derived short name matches exactly the long file name. If so
349            we don't need to mangle the name with a numeric value based on its entry.  */
350         if (match == FX_FALSE)
351         {
352 
353             /* Name does not match, create a mangled name.  */
354 
355             /* Generate short file name from LFN.  */
356             entry = entry_ptr -> fx_dir_entry_number;
357 
358             /* Name suffice is between 000 and FFFF in hex, calculate this short file
359                name's numeric component.  */
360             entry = entry % 0x10000;
361 
362             /* Build short name of the format xxx~NNNN.ext.  */
363             if (i > 3)
364             {
365                 i = 3;
366             }
367             shortname[i++] = '~';
368 
369             /* Loop to build the numeric part of the name.  */
370             for (l = 0; l < 4; l++)
371             {
372 
373                 /* Shift down the entry number based on the numeric position.  */
374                 if (l == 0)
375                 {
376                     temp =  ((entry >> 12) & 0xf);
377                 }
378                 else if (l == 1)
379                 {
380                     temp = ((entry >> 8) & 0xf);
381                 }
382                 else if (l == 2)
383                 {
384                     temp = ((entry >> 4) & 0xf);
385                 }
386                 else
387                 {
388                     temp = ((entry) & 0xf);
389                 }
390 
391                 /* Now build hex value.  */
392                 if (temp > 9)
393                     shortname[i++] =  (CHAR)('A' + (temp - 10));
394                 else
395                     shortname[i++] =  (CHAR)('0' + temp);
396             }
397         }
398 
399         /* Set end of short string to NULL.   */
400         shortname[11] = 0;
401 
402         /* Determine if the first character of the short file name is the directory free
403            value. If so, it must be changed.  */
404         if (((UCHAR)shortname[0] == (UCHAR)FX_DIR_ENTRY_FREE) && (delete_flag == FX_FALSE))
405         {
406 
407             /* Change to 0x8F to be compatible with what DOS does.  */
408             shortname[0] =  (CHAR)0x8F;
409         }
410 
411         /* Loop to convert the new short file name to upper case.  */
412         for (i = 0; i < (FX_DIR_NAME_SIZE + FX_DIR_EXT_SIZE); i++)
413         {
414 
415             /* Pickup shortname character.  */
416             alpha = shortname[i];
417 
418             /* Determine if character is lower case.  */
419             if ((alpha >= 'a') && (alpha <= 'z'))
420             {
421 
422                 /* Store the character - converted to upper case.  */
423                 alpha =  (CHAR)(alpha - ((CHAR)0x20));
424             }
425 
426             /* Now store the short name character.  */
427             shortname[i] =  alpha;
428         }
429 
430         /* Determine if there already is a short name and we are not deleting the entry.  */
431         if (entry_ptr -> fx_dir_entry_short_name[0] != 0)
432         {
433 
434             /* Yes, override the calculated shortname with the original 8.3 name.  */
435 
436             /* Clear the short file name area.  */
437             for (i = 0; i < FX_DIR_NAME_SIZE + FX_DIR_EXT_SIZE; i++)
438             {
439                 shortname[i] = ' ';
440             }
441 
442             /* Loop to copy the original short file name.  */
443             for (i = 0, j = 0; j < FX_DIR_NAME_SIZE; i++, j++)
444             {
445 
446                 /* Check for end of copy conditions.  */
447                 if ((UCHAR)entry_ptr -> fx_dir_entry_short_name[i] == '.')
448                 {
449                     break;
450                 }
451                 if ((UCHAR)entry_ptr -> fx_dir_entry_short_name[i] == 0)
452                 {
453                     break;
454                 }
455 
456                 /* Pickup the character.  */
457                 alpha =  entry_ptr -> fx_dir_entry_short_name[i];
458 
459                 /* Copy file name character.  */
460                 shortname[j] =  alpha;
461             }
462 
463             /* Determine if there is anything left in the short file name.  */
464             if ((UCHAR)entry_ptr -> fx_dir_entry_short_name[i] != 0)
465             {
466 
467                 /* Pickup remaining characters.  */
468                 for (i++, j = FX_DIR_NAME_SIZE; j < FX_DIR_NAME_SIZE + FX_DIR_EXT_SIZE; i++, j++)
469                 {
470 
471                     /* If NULL is encountered, stop the copying.  */
472                     if ((UCHAR)entry_ptr -> fx_dir_entry_short_name[i] == 0)
473                     {
474                         break;
475                     }
476 
477                     /* Pickup the character.  */
478                     alpha =  entry_ptr -> fx_dir_entry_short_name[i];
479 
480                     /* Copy file name character.  */
481                     shortname[j] =  alpha;
482                 }
483             }
484 
485             /* Loop to make sure the short name is upper case.  */
486             for (j = 0; j < (FX_DIR_NAME_SIZE + FX_DIR_EXT_SIZE); j++)
487             {
488 
489                 /* Pickup the character.  */
490                 alpha =  shortname[j];
491 
492                 /* Determine if character is lower case.  */
493                 if ((alpha >= 'a') && (alpha <= 'z'))
494                 {
495 
496                     /* Store the character - converted to upper case.  */
497                     alpha =  (CHAR)(alpha - ((CHAR)0x20));
498                 }
499 
500                 /* Copy file name character.  */
501                 shortname[j] =  alpha;
502             }
503 
504             /* Determine if the first character of the short file name is the directory free
505                value. If so, it must be changed.  */
506             if (((UCHAR)shortname[0]) == ((UCHAR)FX_DIR_ENTRY_FREE))
507             {
508 
509                 /* Change to 0x8F to be compatible with what DOS does.  */
510                 shortname[0] =  (CHAR)0x8F;
511             }
512         }
513 
514         /* Loop to calculate the checksum.  */
515         for (i = checksum = 0; i < FX_DIR_NAME_SIZE + FX_DIR_EXT_SIZE; i++)
516         {
517 
518             /* Calculate the checksum.  */
519             checksum = (UCHAR)((UCHAR)(((checksum & 1) << 7) | ((checksum & (UCHAR)0xfe) >> 1)) + shortname[i]);
520         }
521 
522         /* Set the last entry mark.  */
523         work_ptr[0] =  (UCHAR)(0x40 | card);
524 
525         /* Loop to process remainder of long file name entry.  */
526         while (card > 0)
527         {
528 
529             /* Clear eof marker.  */
530             eof_marker = 0;
531 
532             /* Determine if the entry is free.  */
533             if ((UCHAR)shortname[0] == (UCHAR)FX_DIR_ENTRY_FREE)
534             {
535                 /* Yes, place delete marker.  */
536                 work_ptr[0] =  (UCHAR)FX_DIR_ENTRY_FREE;
537             }
538 
539             /* Setup various long file name fields.  */
540             work_ptr[11] = FX_LONG_NAME;
541             work_ptr[12] = 0;
542             work_ptr[13] = checksum;
543             work_ptr[26] = 0;
544             work_ptr[27] = 0;
545 
546             /* Loop through file name fields.  */
547             for (i = 1, j = 13 * (card - 1); i < FX_DIR_ENTRY_SIZE; i += 2)
548             {
549 
550                 /* Process relative to specific fields.  */
551                 if ((i == 11) || (i == 26))
552                 {
553                     continue;
554                 }
555 
556                 if (i == 13)
557                 {
558                     i = 12;
559                     continue;
560                 }
561 
562                 /* Determine if the EOF marker is present.  */
563                 if (eof_marker)
564                 {
565 
566                     work_ptr[i] = eof_marker;
567                     work_ptr[i + 1] = eof_marker;
568                 }
569                 else
570                 {
571                     work_ptr[i] = (UCHAR)entry_ptr -> fx_dir_entry_name[j];
572                     work_ptr[i + 1] = 0;
573                 }
574 
575                 if (entry_ptr -> fx_dir_entry_name[j] == 0)
576                 {
577 
578                     /* end of name, pad with 0xff.  */
579                     eof_marker =  (UCHAR)0xff;
580                 }
581 
582                 j++;
583             }
584 
585             /* Move to the next directory entry.  */
586             work_ptr += FX_DIR_ENTRY_SIZE;
587             byte_offset += FX_DIR_ENTRY_SIZE;
588 
589 #ifdef FX_ENABLE_FAULT_TOLERANT
590             /* Update changed_size. */
591             changed_size += FX_DIR_ENTRY_SIZE;
592 #endif /* FX_ENABLE_FAULT_TOLERANT */
593 
594             /* Determine if the entry overlaps into the next sector.  */
595             if (byte_offset >= media_ptr -> fx_media_bytes_per_sector)
596             {
597 #ifdef FX_ENABLE_FAULT_TOLERANT
598                 if (media_ptr -> fx_media_fault_tolerant_enabled)
599                 {
600 
601                     /* Redirect this request to log file. */
602                     status = _fx_fault_tolerant_add_dir_log(media_ptr, logical_sector, changed_offset, changed_ptr, changed_size);
603                 }
604                 else
605                 {
606 #endif /* FX_ENABLE_FAULT_TOLERANT */
607 
608                     /* Write current logical sector out.  */
609                     status =  _fx_utility_logical_sector_write(media_ptr, (ULONG64) logical_sector,
610                                                                sector_base_ptr, ((ULONG) 1), FX_DIRECTORY_SECTOR);
611 #ifdef FX_ENABLE_FAULT_TOLERANT
612                 }
613 #endif /* FX_ENABLE_FAULT_TOLERANT */
614 
615                 /* Determine if an error occurred.  */
616                 if (status != FX_SUCCESS)
617                 {
618 
619                     /* Return the error status.  */
620                     return(status);
621                 }
622 
623                 /* Determine if we are in the root directory.  */
624                 if (logical_sector >= (ULONG)(media_ptr -> fx_media_data_sector_start))
625                 {
626 
627                     /* Determine the next sector of the directory entry.  */
628                     if (relative_sector < (media_ptr -> fx_media_sectors_per_cluster - 1))
629                     {
630 
631                         /* More sectors in this cluster.  */
632 
633                         /* Simply increment the logical sector.  */
634                         logical_sector++;
635 
636                         /* Increment the relative sector.  */
637                         relative_sector++;
638                     }
639                     else
640                     {
641 
642                         /* We need to move to the next cluster.  */
643 
644                         /* Pickup the next cluster.  */
645                         status =  _fx_utility_FAT_entry_read(media_ptr, cluster, &next_cluster);
646 
647                         /* Check for I/O error.  */
648                         if (status != FX_SUCCESS)
649                         {
650 
651                             /* Return error code.  */
652                             return(status);
653                         }
654 
655                         /* Copy next cluster to the current cluster.  */
656                         cluster =  next_cluster;
657 
658                         /* Check the value of the new cluster - it must be a valid cluster number
659                            or something is really wrong!  */
660                         if ((cluster < FX_FAT_ENTRY_START) || (cluster >= media_ptr -> fx_media_fat_reserved))
661                         {
662 
663                             /* Send error message back to caller.  */
664                             return(FX_FILE_CORRUPT);
665                         }
666 
667                         /* Setup the relative sector (this is zero for subsequent cluster.  */
668                         relative_sector =  0;
669 
670                         /* Calculate the next logical sector.  */
671                         logical_sector =   ((ULONG)media_ptr -> fx_media_data_sector_start) +
672                             (((ULONG)cluster - FX_FAT_ENTRY_START) *
673                              ((ULONG)media_ptr -> fx_media_sectors_per_cluster));
674                     }
675                 }
676                 else
677                 {
678 
679                     /* Increment the logical sector.  */
680                     logical_sector++;
681 
682                     /* Determine if the logical sector is valid.  */
683                     if (logical_sector >= (ULONG)(media_ptr -> fx_media_data_sector_start))
684                     {
685 
686                         /* We have exceeded the root directory.  */
687 
688                         /* Send error message back to caller.  */
689                         return(FX_FILE_CORRUPT);
690                     }
691                 }
692 
693                 /* Read the sector.  */
694                 status =  _fx_utility_logical_sector_read(media_ptr, (ULONG64) logical_sector,
695                                                           media_ptr -> fx_media_memory_buffer, ((ULONG) 1), FX_DIRECTORY_SECTOR);
696 
697                 /* Determine if an error occurred.  */
698                 if (status != FX_SUCCESS)
699                 {
700 
701                     /* Return the error status.  */
702                     return(status);
703                 }
704 
705                 /* Setup logical sector.  */
706                 sector_base_ptr = media_ptr -> fx_media_memory_buffer;
707 
708                 /* Setup a fresh byte offset.  */
709                 byte_offset = 0;
710 
711                 /* Setup a new pointer into the buffer.  */
712                 work_ptr = sector_base_ptr;
713 
714 #ifdef FX_ENABLE_FAULT_TOLERANT
715                 /* Initialize data for fault tolerant. */
716                 changed_ptr = work_ptr;
717                 changed_size = 0;
718                 changed_offset = 0;
719 #endif /* FX_ENABLE_FAULT_TOLERANT */
720             }
721 
722             /* Decrement loop control.  */
723             card--;
724             work_ptr[0] = (UCHAR)card;
725         }
726 
727         /* Determine if there is a short name.  */
728         if (entry_ptr -> fx_dir_entry_short_name[0] == 0)
729         {
730 
731             /* Loop to copy the new short file name.  */
732             for (i = 0; i < (FX_DIR_NAME_SIZE + FX_DIR_EXT_SIZE); i++)
733             {
734 
735                 /* Pickup shortname character.  */
736                 alpha = shortname[i];
737 
738                 /* Now store the short name character.  */
739                 *work_ptr++ =  (UCHAR)alpha;
740             }
741         }
742         else
743         {
744 
745             /* Clear the short file name area.  */
746             for (i = 0; i < FX_DIR_NAME_SIZE + FX_DIR_EXT_SIZE; i++)
747             {
748                 work_ptr[i] = ' ';
749             }
750 
751             /* Loop to copy the old short file name.  */
752             for (i = 0, j = 0; j < FX_DIR_NAME_SIZE; i++, j++)
753             {
754 
755                 /* Check for end of copy conditions.  */
756                 if ((UCHAR)entry_ptr -> fx_dir_entry_short_name[i] == '.')
757                 {
758                     break;
759                 }
760                 if ((UCHAR)entry_ptr -> fx_dir_entry_short_name[i] == 0)
761                 {
762                     break;
763                 }
764 
765                 /* Copy file name character.  */
766                 work_ptr[j] =  (UCHAR)entry_ptr -> fx_dir_entry_short_name[i];
767             }
768 
769             /* Determine if there is anything left in the short file name.  */
770             if ((UCHAR)entry_ptr -> fx_dir_entry_short_name[i] != 0)
771             {
772 
773                 /* Pickup remaining characters.  */
774                 for (i++, j = FX_DIR_NAME_SIZE; j < FX_DIR_NAME_SIZE + FX_DIR_EXT_SIZE; i++, j++)
775                 {
776 
777                     /* If NULL is encountered, stop the copying.  */
778                     if ((UCHAR)entry_ptr -> fx_dir_entry_short_name[i] == 0)
779                     {
780                         break;
781                     }
782 
783                     /* Copy file name character.  */
784                     work_ptr[j] =  (UCHAR)entry_ptr -> fx_dir_entry_short_name[i];
785                 }
786             }
787 
788             /* Adjust the work pointer accordingly.  */
789             work_ptr += (FX_DIR_NAME_SIZE + FX_DIR_EXT_SIZE);
790         }
791     }
792     else
793     {
794 
795         /* Determine if long name was shorted.  */
796         if (entry_ptr -> fx_dir_entry_long_name_shorted > 0)
797         {
798 
799             /* Check for a valid short name.  */
800             if ((UCHAR)(0x40 | entry_ptr -> fx_dir_entry_long_name_shorted) == (UCHAR)(*work_ptr))
801             {
802 
803                 /* Loop through the file name.  */
804                 for (j = 0; j < entry_ptr -> fx_dir_entry_long_name_shorted; j++)
805                 {
806 
807                     /* Check for a free entry to be written.  */
808                     if ((UCHAR)entry_ptr -> fx_dir_entry_name[0] == (UCHAR)FX_DIR_ENTRY_FREE)
809                     {
810                         /* Delete long parts.  */
811                         work_ptr[0] =  (UCHAR)FX_DIR_ENTRY_FREE;
812                     }
813 
814                     /* Setup pointers for the name write.  */
815                     work_ptr += FX_DIR_ENTRY_SIZE;
816                     byte_offset += FX_DIR_ENTRY_SIZE;
817 
818 #ifdef FX_ENABLE_FAULT_TOLERANT
819                     /* Update changed_size. */
820                     changed_size += FX_DIR_ENTRY_SIZE;
821 #endif /* FX_ENABLE_FAULT_TOLERANT */
822 
823                     /* Determine if the write is within the current sector.   */
824                     if (byte_offset >= media_ptr -> fx_media_bytes_per_sector)
825                     {
826 #ifdef FX_ENABLE_FAULT_TOLERANT
827                         if (media_ptr -> fx_media_fault_tolerant_enabled)
828                         {
829 
830                             /* Redirect this request to log file. */
831                             status = _fx_fault_tolerant_add_dir_log(media_ptr, (ULONG64) logical_sector, changed_offset, changed_ptr, changed_size);
832                         }
833                         else
834                         {
835 #endif /* FX_ENABLE_FAULT_TOLERANT */
836 
837                             /* Write the current sector out.  */
838                             status =  _fx_utility_logical_sector_write(media_ptr, (ULONG64) logical_sector,
839                                                                        sector_base_ptr, ((ULONG) 1), FX_DIRECTORY_SECTOR);
840 #ifdef FX_ENABLE_FAULT_TOLERANT
841                         }
842 #endif /* FX_ENABLE_FAULT_TOLERANT */
843 
844                         /* Determine if an error occurred.  */
845                         if (status != FX_SUCCESS)
846                         {
847 
848                             /* Return the error status.  */
849                             return(status);
850                         }
851 
852                         /* Determine if we are in the root directory.  */
853                         if (logical_sector >= (ULONG)(media_ptr -> fx_media_data_sector_start))
854                         {
855 
856                             /* Determine the next sector of the directory entry.  */
857                             if (relative_sector < (media_ptr -> fx_media_sectors_per_cluster - 1))
858                             {
859 
860                                 /* More sectors in this cluster.  */
861 
862                                 /* Simply increment the logical sector.  */
863                                 logical_sector++;
864 
865                                 /* Increment the relative sector.  */
866                                 relative_sector++;
867                             }
868                             else
869                             {
870 
871                                 /* We need to move to the next cluster.  */
872 
873                                 /* Pickup the next cluster.  */
874                                 status =  _fx_utility_FAT_entry_read(media_ptr, cluster, &next_cluster);
875 
876                                 /* Check for I/O error.  */
877                                 if (status != FX_SUCCESS)
878                                 {
879 
880                                     /* Return error code.  */
881                                     return(status);
882                                 }
883 
884                                 /* Copy next cluster to the current cluster.  */
885                                 cluster =  next_cluster;
886 
887                                 /* Check the value of the new cluster - it must be a valid cluster number
888                                    or something is really wrong!  */
889                                 if ((cluster < FX_FAT_ENTRY_START) || (cluster >= media_ptr -> fx_media_fat_reserved))
890                                 {
891 
892                                     /* Send error message back to caller.  */
893                                     return(FX_FILE_CORRUPT);
894                                 }
895 
896                                 /* Setup the relative sector (this is zero for subsequent cluster.  */
897                                 relative_sector =  0;
898 
899                                 /* Calculate the next logical sector.  */
900                                 logical_sector =   ((ULONG)media_ptr -> fx_media_data_sector_start) +
901                                     (((ULONG)cluster - FX_FAT_ENTRY_START) *
902                                      ((ULONG)media_ptr -> fx_media_sectors_per_cluster));
903                             }
904                         }
905                         else
906                         {
907 
908                             /* Increment the logical sector.  */
909                             logical_sector++;
910 
911                             /* Determine if the logical sector is valid.  */
912                             if (logical_sector >= (ULONG)(media_ptr -> fx_media_data_sector_start))
913                             {
914 
915                                 /* We have exceeded the root directory.  */
916 
917                                 /* Send error message back to caller.  */
918                                 return(FX_FILE_CORRUPT);
919                             }
920                         }
921 
922                         /* Read the next logical sector.  */
923                         status =  _fx_utility_logical_sector_read(media_ptr, (ULONG64) logical_sector,
924                                                                   media_ptr -> fx_media_memory_buffer, ((ULONG) 1), FX_DIRECTORY_SECTOR);
925 
926                         /* Determine if an error occurred.  */
927                         if (status != FX_SUCCESS)
928                         {
929 
930                             /* Return the error status.  */
931                             return(status);
932                         }
933 
934                         /* Move to the next sector buffer.  */
935                         sector_base_ptr = media_ptr -> fx_media_memory_buffer;
936 
937                         /* Setup new buffer pointers.  */
938                         byte_offset =  0;
939                         work_ptr = sector_base_ptr;
940 
941 #ifdef FX_ENABLE_FAULT_TOLERANT
942                         /* Initialize data for fault tolerant. */
943                         changed_ptr = work_ptr;
944                         changed_size = 0;
945                         changed_offset = 0;
946 #endif /* FX_ENABLE_FAULT_TOLERANT */
947                     }
948                 }
949             }
950         }
951 
952         /* This is an 8.3 name.  First clear the directory name.  */
953         for (j = 0; j < FX_DIR_NAME_SIZE + FX_DIR_EXT_SIZE; j++)
954         {
955             work_ptr[j] = ' ';
956         }
957 
958         /* Copy leading dots in case of first two entries of a directory.  */
959         for (i = 0; (UCHAR)entry_ptr -> fx_dir_entry_name[i] == '.'; i++)
960         {
961             work_ptr[i] = '.';
962         }
963 
964         /* Determine if there are more characters to copy.  */
965         if ((UCHAR)entry_ptr -> fx_dir_entry_name[i] != 0)
966         {
967 
968             /* Copy directory name.  */
969             for (i = 0, j = 0; j < FX_DIR_NAME_SIZE; i++, j++)
970             {
971 
972                 /* Check for end of copy conditions.  */
973                 if ((UCHAR)entry_ptr -> fx_dir_entry_name[i] == '.')
974                 {
975                     break;
976                 }
977                 if ((UCHAR)entry_ptr -> fx_dir_entry_name[i] == 0)
978                 {
979                     break;
980                 }
981 
982                 /* Pickup shortname character.  */
983                 alpha = entry_ptr -> fx_dir_entry_name[i];
984 
985                 /* Determine if character is lower case.  */
986                 if ((alpha >= 'a') && (alpha <= 'z'))
987                 {
988 
989                     /* Store the character - converted to upper case.  */
990                     alpha =  (CHAR)(alpha - ((CHAR)0x20));
991                 }
992 
993                 /* Copy a name character.  */
994                 work_ptr[j] =  (UCHAR)alpha;
995             }
996         }
997 
998         /* Determine if there are more characters in the name.  */
999         if ((UCHAR)entry_ptr -> fx_dir_entry_name[i] != 0)
1000         {
1001 
1002             /* Loop to copy the remainder of the name.  */
1003             for (i++, j = FX_DIR_NAME_SIZE; j < FX_DIR_NAME_SIZE + FX_DIR_EXT_SIZE; i++, j++)
1004             {
1005 
1006                 /* Check for end of copy conditions.  */
1007                 if ((UCHAR)entry_ptr -> fx_dir_entry_name[i] == 0)
1008                 {
1009                     break;
1010                 }
1011 
1012                 /* Pickup shortname character.  */
1013                 alpha = entry_ptr -> fx_dir_entry_name[i];
1014 
1015                 /* Determine if character is lower case.  */
1016                 if ((alpha >= 'a') && (alpha <= 'z'))
1017                 {
1018 
1019                     /* Store the character - converted to upper case.  */
1020                     alpha =  (CHAR)(alpha - ((CHAR)0x20));
1021                 }
1022 
1023                 /* Copy a name character.  */
1024                 work_ptr[j] =  (UCHAR)alpha;
1025             }
1026         }
1027 
1028         /* Move to the next entry.  */
1029         work_ptr += (FX_DIR_NAME_SIZE + FX_DIR_EXT_SIZE);
1030     }
1031 
1032     /* Write out the 8.3 part of the name. */
1033 
1034     /* Copy the attribute into the destination.  */
1035     *work_ptr++ =  entry_ptr -> fx_dir_entry_attributes;
1036 
1037     /* Copy the reserved byte.  */
1038     *work_ptr++ =  entry_ptr -> fx_dir_entry_reserved;
1039 
1040     /* Copy the created time in milliseconds.  */
1041     *work_ptr++ =  entry_ptr -> fx_dir_entry_created_time_ms;
1042 
1043     /* Copy the created time.  */
1044     _fx_utility_16_unsigned_write(work_ptr, entry_ptr -> fx_dir_entry_created_time);
1045     work_ptr =  work_ptr + 2;  /* Always 2 bytes  */
1046 
1047     /* Copy the created date.  */
1048     _fx_utility_16_unsigned_write(work_ptr, entry_ptr -> fx_dir_entry_created_date);
1049     work_ptr =  work_ptr + 2;  /* Always 2 bytes  */
1050 
1051     /* Copy the last accessed date.  */
1052     _fx_utility_16_unsigned_write(work_ptr, entry_ptr -> fx_dir_entry_last_accessed_date);
1053     work_ptr =  work_ptr + 2;  /* Always 2 bytes  */
1054 
1055     /* Determine if a FAT32 entry is present.  */
1056     if (media_ptr -> fx_media_32_bit_FAT)
1057     {
1058 
1059         /* Yes, FAT32 is present, store upper half of cluster.  */
1060         temp = (entry_ptr -> fx_dir_entry_cluster >> 16);
1061         _fx_utility_16_unsigned_write(work_ptr, temp);
1062     }
1063     else
1064     {
1065 
1066         /* No, FAT16 or FAT12 is present, just write a 0 for
1067            the upper half of the cluster.  */
1068         _fx_utility_16_unsigned_write(work_ptr, 0);
1069     }
1070 
1071     /* Advance the entry pointer.  */
1072     work_ptr =  work_ptr + 2;  /* Always 2 bytes  */
1073 
1074     /* Copy the time into the destination.  */
1075     _fx_utility_16_unsigned_write(work_ptr, entry_ptr -> fx_dir_entry_time);
1076     work_ptr =  work_ptr + 2;  /* Always 2 bytes  */
1077 
1078     /* Copy the date into the destination.  */
1079     _fx_utility_16_unsigned_write(work_ptr, entry_ptr -> fx_dir_entry_date);
1080     work_ptr =  work_ptr + 2;  /* Always 2 bytes  */
1081 
1082     /* Copy the starting cluster into the destination.  */
1083     _fx_utility_16_unsigned_write(work_ptr, (UINT)entry_ptr -> fx_dir_entry_cluster);
1084     work_ptr =  work_ptr + 2;  /* Always 2 bytes  */
1085 
1086     /* Copy the file size into the destination.  */
1087     _fx_utility_32_unsigned_write(work_ptr, (ULONG)entry_ptr -> fx_dir_entry_file_size);
1088 
1089 #ifdef FX_ENABLE_FAULT_TOLERANT
1090     /* Update changed_size. */
1091     changed_size += FX_DIR_ENTRY_SIZE;
1092 
1093     if (media_ptr -> fx_media_fault_tolerant_enabled &&
1094         (media_ptr -> fx_media_fault_tolerant_state & FX_FAULT_TOLERANT_STATE_STARTED))
1095     {
1096 
1097         /* Redirect this request to log file. */
1098         status = _fx_fault_tolerant_add_dir_log(media_ptr, (ULONG64) logical_sector, changed_offset, changed_ptr, changed_size);
1099     }
1100     else
1101     {
1102 #endif /* FX_ENABLE_FAULT_TOLERANT */
1103 
1104         /* Write the directory sector to the media.  */
1105         status =  _fx_utility_logical_sector_write(media_ptr, (ULONG64) logical_sector,
1106                                                    sector_base_ptr, ((ULONG) 1), FX_DIRECTORY_SECTOR);
1107 #ifdef FX_ENABLE_FAULT_TOLERANT
1108     }
1109 #endif /* FX_ENABLE_FAULT_TOLERANT */
1110 
1111     /* Determine if an error occurred.  */
1112     if (status != FX_SUCCESS)
1113     {
1114 
1115         /* Return the error status.  */
1116         return(status);
1117     }
1118 
1119 #ifndef FX_MEDIA_DISABLE_SEARCH_CACHE
1120 
1121     /* Determine if there is a previously found directory entry in the directory
1122        search cache.  */
1123     if (media_ptr -> fx_media_last_found_name[0])
1124     {
1125 
1126         /* Determine if the cached search directory entry matches the directory entry being
1127            written.  */
1128         if ((entry_ptr -> fx_dir_entry_log_sector == media_ptr -> fx_media_last_found_entry.fx_dir_entry_log_sector) &&
1129             (entry_ptr -> fx_dir_entry_byte_offset == media_ptr -> fx_media_last_found_entry.fx_dir_entry_byte_offset))
1130         {
1131 
1132             /* Yes, this entry is the same as the one currently in the directory search cache.
1133                Update various fields in the directory search cache with the information being
1134                written now.  */
1135             media_ptr -> fx_media_last_found_entry.fx_dir_entry_cluster =         entry_ptr -> fx_dir_entry_cluster;
1136             media_ptr -> fx_media_last_found_entry.fx_dir_entry_file_size =       entry_ptr -> fx_dir_entry_file_size;
1137             media_ptr -> fx_media_last_found_entry.fx_dir_entry_attributes =      entry_ptr -> fx_dir_entry_attributes;
1138             media_ptr -> fx_media_last_found_entry.fx_dir_entry_time =            entry_ptr -> fx_dir_entry_time;
1139             media_ptr -> fx_media_last_found_entry.fx_dir_entry_date =            entry_ptr -> fx_dir_entry_date;
1140             media_ptr -> fx_media_last_found_entry.fx_dir_entry_reserved =        entry_ptr -> fx_dir_entry_reserved;
1141             media_ptr -> fx_media_last_found_entry.fx_dir_entry_created_time_ms = entry_ptr -> fx_dir_entry_created_time_ms;
1142             media_ptr -> fx_media_last_found_entry.fx_dir_entry_created_time =    entry_ptr -> fx_dir_entry_created_time;
1143             media_ptr -> fx_media_last_found_entry.fx_dir_entry_created_date =    entry_ptr -> fx_dir_entry_created_date;
1144         }
1145     }
1146 #endif
1147 
1148     /* Return success to the caller.  */
1149     return(FX_SUCCESS);
1150 }
1151 
1152