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 /**   File                                                                */
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_file.h"
31 #include "fx_utility.h"
32 #ifdef FX_ENABLE_FAULT_TOLERANT
33 #include "fx_fault_tolerant.h"
34 #endif /* FX_ENABLE_FAULT_TOLERANT */
35 
36 
37 /**************************************************************************/
38 /*                                                                        */
39 /*  FUNCTION                                               RELEASE        */
40 /*                                                                        */
41 /*    _fx_file_rename                                     PORTABLE C      */
42 /*                                                           6.1          */
43 /*  AUTHOR                                                                */
44 /*                                                                        */
45 /*    William E. Lamie, Microsoft Corporation                             */
46 /*                                                                        */
47 /*  DESCRIPTION                                                           */
48 /*                                                                        */
49 /*    This function first attempts to find the specified file.  If found, */
50 /*    the rename request is valid and the directory entry will be changed */
51 /*    to the new file name.  Otherwise, if the file is not found, the     */
52 /*    appropriate error code is returned to the caller.                   */
53 /*                                                                        */
54 /*  INPUT                                                                 */
55 /*                                                                        */
56 /*    media_ptr                             Media control block pointer   */
57 /*    old_file_name                         Old file name pointer         */
58 /*    new_file_name                         New file name pointer         */
59 /*                                                                        */
60 /*  OUTPUT                                                                */
61 /*                                                                        */
62 /*    return status                                                       */
63 /*                                                                        */
64 /*  CALLS                                                                 */
65 /*                                                                        */
66 /*    _fx_directory_entry_write             Write the new directory entry */
67 /*    _fx_directory_free_search             Search for a free directory   */
68 /*                                            entry in target directory   */
69 /*    _fx_directory_name_extract            Extract the new filename      */
70 /*    _fx_directory_search                  Search for the file name in   */
71 /*                                          the directory structure       */
72 /*    _fx_fault_tolerant_transaction_start  Start fault tolerant          */
73 /*                                            transaction                 */
74 /*    _fx_fault_tolerant_transaction_end    End fault tolerant transaction*/
75 /*    _fx_fault_tolerant_recover            Recover FAT chain             */
76 /*                                                                        */
77 /*  CALLED BY                                                             */
78 /*                                                                        */
79 /*    Application Code                                                    */
80 /*                                                                        */
81 /*  RELEASE HISTORY                                                       */
82 /*                                                                        */
83 /*    DATE              NAME                      DESCRIPTION             */
84 /*                                                                        */
85 /*  05-19-2020     William E. Lamie         Initial Version 6.0           */
86 /*  09-30-2020     William E. Lamie         Modified comment(s),          */
87 /*                                            resulting in version 6.1    */
88 /*                                                                        */
89 /**************************************************************************/
_fx_file_rename(FX_MEDIA * media_ptr,CHAR * old_file_name,CHAR * new_file_name)90 UINT  _fx_file_rename(FX_MEDIA *media_ptr, CHAR *old_file_name, CHAR *new_file_name)
91 {
92 
93 ULONG        i;
94 CHAR        *work_ptr;
95 CHAR         alpha, beta;
96 UINT         status;
97 
98 #ifndef FX_DONT_UPDATE_OPEN_FILES
99 ULONG        open_count;
100 FX_FILE     *search_ptr;
101 #endif
102 CHAR        *new_name_ptr;
103 FX_DIR_ENTRY old_dir_entry, new_dir_entry;
104 FX_DIR_ENTRY search_directory;
105 #ifdef FX_RENAME_PATH_INHERIT
106 UINT         j;
107 #endif
108 UCHAR        not_a_file_attr;
109 
110 
111 #ifndef FX_MEDIA_STATISTICS_DISABLE
112 
113     /* Increment the number of times this service has been called.  */
114     media_ptr -> fx_media_file_renames++;
115 #endif
116 
117     /* Setup pointers to media name buffers.  */
118     old_dir_entry.fx_dir_entry_name =     media_ptr -> fx_media_name_buffer + FX_MAX_LONG_NAME_LEN;
119     new_dir_entry.fx_dir_entry_name =     media_ptr -> fx_media_name_buffer + FX_MAX_LONG_NAME_LEN * 2;
120     search_directory.fx_dir_entry_name =  media_ptr -> fx_media_name_buffer + FX_MAX_LONG_NAME_LEN * 3;
121 
122     /* Clear the short name strings.  */
123     old_dir_entry.fx_dir_entry_short_name[0] =     0;
124     new_dir_entry.fx_dir_entry_short_name[0] =     0;
125     search_directory.fx_dir_entry_short_name[0] =  0;
126 
127     /* Determine if the supplied name is less than the maximum supported name size. The
128        maximum name (FX_MAX_LONG_NAME_LEN) is defined in fx_api.h.  */
129     i =  0;
130     work_ptr =  (CHAR *)new_file_name;
131     while (*work_ptr)
132     {
133 
134         /* Determine if the character designates a new path.  */
135         if ((*work_ptr == '\\') || (*work_ptr == '/'))
136         {
137             /* Yes, reset the name size.  */
138             i =  0;
139         }
140         /* Check for leading spaces.  */
141         else if ((*work_ptr != ' ') || (i != 0))
142         {
143 
144             /* No leading spaces, increment the name size.  */
145             i++;
146         }
147 
148         /* Move to the next character.  */
149         work_ptr++;
150     }
151 
152     /* Determine if the supplied name is valid.  */
153     if ((i == 0) || (i >= FX_MAX_LONG_NAME_LEN))
154     {
155 
156         /* Return an invalid name value.  */
157         return(FX_INVALID_NAME);
158     }
159 
160     /* Check the media to make sure it is open.  */
161     if (media_ptr -> fx_media_id != FX_MEDIA_ID)
162     {
163 
164         /* Return the media not opened error.  */
165         return(FX_MEDIA_NOT_OPEN);
166     }
167 
168     /* If trace is enabled, insert this event into the trace buffer.  */
169     FX_TRACE_IN_LINE_INSERT(FX_TRACE_FILE_RENAME, media_ptr, old_file_name, new_file_name, 0, FX_TRACE_FILE_EVENTS, 0, 0)
170 
171     /* Protect against other threads accessing the media.  */
172     FX_PROTECT
173 
174 #ifdef FX_ENABLE_FAULT_TOLERANT
175     /* Start transaction. */
176     _fx_fault_tolerant_transaction_start(media_ptr);
177 #endif /* FX_ENABLE_FAULT_TOLERANT */
178 
179     /* Check for write protect at the media level (set by driver).  */
180     if (media_ptr -> fx_media_driver_write_protect)
181     {
182 
183 #ifdef FX_ENABLE_FAULT_TOLERANT
184         FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr);
185 #endif /* FX_ENABLE_FAULT_TOLERANT */
186 
187         /* Release media protection.  */
188         FX_UNPROTECT
189 
190         /* Return write protect error.  */
191         return(FX_WRITE_PROTECT);
192     }
193 
194     /* Search the system for the supplied file name.  */
195     status =  _fx_directory_search(media_ptr, old_file_name, &old_dir_entry, &search_directory, FX_NULL);
196 
197     /* Determine if the search was successful.  */
198     if (status != FX_SUCCESS)
199     {
200 
201 #ifdef FX_ENABLE_FAULT_TOLERANT
202         FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr);
203 #endif /* FX_ENABLE_FAULT_TOLERANT */
204 
205         /* Release media protection.  */
206         FX_UNPROTECT
207 
208         /* Return the error code.  */
209         return(status);
210     }
211 
212     not_a_file_attr = FX_DIRECTORY | FX_VOLUME;
213 
214     /* Check to make sure the found entry is a file.  */
215     if (old_dir_entry.fx_dir_entry_attributes & not_a_file_attr)
216     {
217 
218 #ifdef FX_ENABLE_FAULT_TOLERANT
219         FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr);
220 #endif /* FX_ENABLE_FAULT_TOLERANT */
221 
222         /* Release media protection.  */
223         FX_UNPROTECT
224 
225         /* Return the not a file error code.  */
226         return(FX_NOT_A_FILE);
227     }
228 
229 #ifdef FX_RENAME_PATH_INHERIT
230 
231     /* Determine if the source file name has a path and the target file name does not.  */
232     if (((old_file_name[0] == '/') || (old_file_name[0] == '\\')) && (new_file_name[0] != '/') && (new_file_name[0] != '\\'))
233     {
234 
235         /* In this case, we need to prepend the path of the old file name to that of the new file name.  */
236 
237         /* Setup pointer to the rename buffer.  */
238         work_ptr =  (CHAR *)media_ptr -> fx_media_rename_buffer;
239 
240         /* First, copy the path of the old file name.  */
241         i =  0;
242         j =  0;
243         while ((old_file_name[i]) && (i < FX_MAXIMUM_PATH))
244         {
245 
246             /* Copy a character into the rename buffer.  */
247             *work_ptr++ =  old_file_name[i];
248 
249             /* Determine if this character is directory separator.  */
250             if ((old_file_name[i] == '/') || (old_file_name[i] == '\\'))
251             {
252 
253                 /* Yes, directory separator has been found - remember the index.  */
254                 j =  i;
255             }
256 
257             /* Move to next position in the old file name.  */
258             i++;
259         }
260 
261         /* At this point, we have the path stored in the rename buffer.  */
262 
263         /* Position past the last slash or backslash.  */
264         j++;
265 
266         /* Reset the working pointer to the position after the last directory separator.  */
267         work_ptr =  (CHAR *)&(media_ptr -> fx_media_rename_buffer[j]);
268 
269         /* Now copy the new file name into the rename buffer.  */
270         i =  0;
271         while ((new_file_name[i]) && (j < FX_MAXIMUM_PATH))
272         {
273 
274             /* Copy a character into the rename buffer.  */
275             *work_ptr++ =  new_file_name[i];
276 
277             /* Move to next character.  */
278             i++;
279             j++;
280         }
281 
282         /* Determine if the path was successfully prepended.  */
283         if (new_file_name[i])
284         {
285 
286             /* No, there was not enough room in the destination buffer.  */
287 
288 #ifdef FX_ENABLE_FAULT_TOLERANT
289             FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr);
290 #endif /* FX_ENABLE_FAULT_TOLERANT */
291 
292             /* Release media protection.  */
293             FX_UNPROTECT
294 
295             /* Return the invalid path error code.  */
296             return(FX_INVALID_PATH);
297         }
298 
299         /* Place a NULL at the end of the string.  */
300         *work_ptr =  (CHAR)FX_NULL;
301 
302         /* At this point, we have successfully prepended the path in the new file name, override
303            the new file name so it is used from now on.  */
304         new_file_name =  (CHAR *)media_ptr -> fx_media_rename_buffer;
305     }
306 #endif
307 
308     /* Search the target directory for the same file name.  */
309     status = _fx_directory_search(media_ptr, new_file_name, &new_dir_entry, &search_directory, &new_name_ptr);
310 
311     /* Determine if the name already exists.  */
312     if (status == FX_SUCCESS)
313     {
314 
315         /* Determine if the new name simply has an ASCII case change. If so, simply let the processing
316            continue.  */
317         i =  0;
318         do
319         {
320 
321             /* Pickup an old name and new name character and convert to upper case if necessary.  */
322             alpha =  old_file_name[i];
323             if ((alpha >= 'a') && (alpha <= 'z'))
324             {
325 
326                 /* Lower case, convert to upper case!  */
327                 alpha =  (CHAR)((INT)alpha - 0x20);
328             }
329             beta =   new_file_name[i];
330             if ((beta >= 'a') && (beta <= 'z'))
331             {
332 
333                 /* Lower case, convert to upper case!  */
334                 beta = (CHAR)((INT)beta - 0x20);
335             }
336 
337             /* Now compare the characters.  */
338             if ((alpha != beta) || (alpha == 0))
339             {
340 
341                 /* Get out of this loop!  */
342                 break;
343             }
344 
345             /* Move to next character.  */
346             i++;
347         } while (i < (FX_MAXIMUM_PATH-1));
348 
349         /* Now determine if the names match.  */
350         if (alpha != beta)
351         {
352 
353             /* No, the names do not match so simply return an error
354                to the caller.  */
355 
356 #ifdef FX_ENABLE_FAULT_TOLERANT
357             FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr);
358 #endif /* FX_ENABLE_FAULT_TOLERANT */
359 
360             /* Release media protection.  */
361             FX_UNPROTECT
362 
363             /* Return the not a file error code.  */
364             return(FX_ALREADY_CREATED);
365         }
366     }
367 
368     /* Change the file name and look for extra stuff at the end.  */
369     if (_fx_directory_name_extract(new_name_ptr, &new_dir_entry.fx_dir_entry_name[0]))
370     {
371 
372 #ifdef FX_ENABLE_FAULT_TOLERANT
373         FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr);
374 #endif /* FX_ENABLE_FAULT_TOLERANT */
375 
376         /* Release media protection.  */
377         FX_UNPROTECT
378 
379         /* Invalid name, return error status.  */
380         return(FX_INVALID_NAME);
381     }
382 
383     /* Search for a free spot in the target directory.  */
384     status = _fx_directory_free_search(media_ptr, &search_directory, &new_dir_entry);
385 
386     /* Determine if a free spot was found.  */
387     if (status != FX_SUCCESS)
388     {
389 
390 #ifdef FX_ENABLE_FAULT_TOLERANT
391         FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr);
392 #endif /* FX_ENABLE_FAULT_TOLERANT */
393 
394         /* Release media protection.  */
395         FX_UNPROTECT
396 
397         /* Return the error code.  */
398         return(status);
399     }
400 
401     /* Extract the new file name.  */
402     _fx_directory_name_extract(new_name_ptr, &new_dir_entry.fx_dir_entry_name[0]);
403 
404     /* Determine if a long name is present.  */
405     if (new_dir_entry.fx_dir_entry_long_name_present)
406     {
407 
408         /* Yes, clear the short file name.  */
409         new_dir_entry.fx_dir_entry_short_name[0] =  0;
410     }
411 
412     /* Save the updated directory parameters.  */
413     new_dir_entry.fx_dir_entry_attributes =          old_dir_entry.fx_dir_entry_attributes;
414     new_dir_entry.fx_dir_entry_cluster    =          old_dir_entry.fx_dir_entry_cluster;
415     new_dir_entry.fx_dir_entry_file_size  =          old_dir_entry.fx_dir_entry_file_size;
416 
417     /* Save the reserved field.  */
418     new_dir_entry.fx_dir_entry_reserved =            old_dir_entry.fx_dir_entry_reserved;
419 
420     /* Set time and date stamps.  */
421     new_dir_entry.fx_dir_entry_created_time_ms =     old_dir_entry.fx_dir_entry_created_time_ms;
422     new_dir_entry.fx_dir_entry_created_time =        old_dir_entry.fx_dir_entry_created_time;
423     new_dir_entry.fx_dir_entry_created_date =        old_dir_entry.fx_dir_entry_created_date;
424     new_dir_entry.fx_dir_entry_last_accessed_date =  old_dir_entry.fx_dir_entry_last_accessed_date;
425     new_dir_entry.fx_dir_entry_time =                old_dir_entry.fx_dir_entry_time;
426     new_dir_entry.fx_dir_entry_date =                old_dir_entry.fx_dir_entry_date;
427 
428 
429     /* Is there a leading dot?  */
430     if (new_dir_entry.fx_dir_entry_name[0] == '.')
431     {
432 
433         /* Yes, toggle the hidden attribute bit.  */
434         new_dir_entry.fx_dir_entry_attributes |=  FX_HIDDEN;
435     }
436 
437 #ifndef FX_MEDIA_DISABLE_SEARCH_CACHE
438 
439     /* Invalidate the directory cache.  */
440     media_ptr -> fx_media_last_found_name[0] =  FX_NULL;
441 #endif
442 
443     /* Now write out the directory entry.  */
444     status =  _fx_directory_entry_write(media_ptr, &new_dir_entry);
445 
446     /* Determine if the write was successful.  */
447     if (status != FX_SUCCESS)
448     {
449 
450 #ifdef FX_ENABLE_FAULT_TOLERANT
451         FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr);
452 #endif /* FX_ENABLE_FAULT_TOLERANT */
453 
454         /* Release media protection.  */
455         FX_UNPROTECT
456 
457         /* Return the error code.  */
458         return(status);
459     }
460 
461 #ifndef FX_DONT_UPDATE_OPEN_FILES
462 
463     /* Search the opened files to update any currently opened files.  */
464     open_count =  media_ptr -> fx_media_opened_file_count;
465     search_ptr =  media_ptr -> fx_media_opened_file_list;
466     while (open_count)
467     {
468 
469         /* Look at each opened file to see if it matches the file being renamed.  */
470         if ((search_ptr -> fx_file_dir_entry.fx_dir_entry_log_sector ==
471              old_dir_entry.fx_dir_entry_log_sector) &&
472             (search_ptr -> fx_file_dir_entry.fx_dir_entry_byte_offset ==
473              old_dir_entry.fx_dir_entry_byte_offset))
474         {
475 
476             /* Yes, the file being renamed is already open.  Update the file's
477                information so that it is kept current.  */
478             search_ptr -> fx_file_dir_entry.fx_dir_entry_cluster =      new_dir_entry.fx_dir_entry_cluster;
479             search_ptr -> fx_file_dir_entry.fx_dir_entry_file_size =    new_dir_entry.fx_dir_entry_file_size;
480             search_ptr -> fx_file_dir_entry.fx_dir_entry_log_sector =   new_dir_entry.fx_dir_entry_log_sector;
481             search_ptr -> fx_file_dir_entry.fx_dir_entry_byte_offset =  new_dir_entry.fx_dir_entry_byte_offset;
482 
483             /* Copy the new name into the file's name buffer.  */
484             i =  0;
485             while (i < (FX_MAX_LONG_NAME_LEN - 1))
486             {
487 
488                 /* Copy byte of the new name.  */
489                 search_ptr -> fx_file_dir_entry.fx_dir_entry_name[i] =  new_dir_entry.fx_dir_entry_name[i];
490 
491                 /* Move to the next character.  */
492                 i++;
493 
494                 /* Determine if we are at the end of the name.  */
495                 if (new_dir_entry.fx_dir_entry_name[i] == FX_NULL)
496                 {
497 
498                     /* Determine if we are not at the maximum name size.  */
499                     if (i < (FX_MAX_LONG_NAME_LEN - 1))
500                     {
501 
502                         /* Get out of the loop.   */
503                         break;
504                     }
505                 }
506             }
507 
508             /* Set the NULL termination in the copy of the new name.  */
509             search_ptr -> fx_file_dir_entry.fx_dir_entry_name[i] =  FX_NULL;
510         }
511 
512         /* Adjust the pointer and decrement the search count.  */
513         search_ptr =  search_ptr -> fx_file_opened_next;
514         open_count--;
515     }
516 #endif
517 
518     /* Now we are ready to remove the old directory entry.  */
519     old_dir_entry.fx_dir_entry_name[0] =        (CHAR)FX_DIR_ENTRY_FREE;
520     old_dir_entry.fx_dir_entry_short_name[0] =  (CHAR)FX_DIR_ENTRY_FREE;
521 
522     /* Now wipe out the old directory entry.  */
523     status =  _fx_directory_entry_write(media_ptr, &old_dir_entry);
524 
525 #ifdef FX_ENABLE_FAULT_TOLERANT
526     /* Check for a bad status.  */
527     if (status != FX_SUCCESS)
528     {
529 
530         FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr);
531 
532         /* Release media protection.  */
533         FX_UNPROTECT
534 
535         /* Return the bad status.  */
536         return(status);
537     }
538 
539     /* End transaction. */
540     status = _fx_fault_tolerant_transaction_end(media_ptr);
541 #endif /* FX_ENABLE_FAULT_TOLERANT */
542 
543     /* Release media protection.  */
544     FX_UNPROTECT
545 
546     /* File rename is complete, return status.  */
547     return(status);
548 }
549 
550