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_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_directory_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 directory.       */
50 /*    If found, the rename request is valid and the directory will be     */
51 /*    changed to the new name.  Otherwise, if the directory is not found, */
52 /*    the appropriate error code is returned to the caller.               */
53 /*                                                                        */
54 /*  INPUT                                                                 */
55 /*                                                                        */
56 /*    media_ptr                             Media control block pointer   */
57 /*    old_directory_name                    Old file directory pointer    */
58 /*    new_directory_name                    New file directory 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                       */
69 /*    _fx_directory_name_extract            Extract directory name        */
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 /*    _fx_fault_tolerant_reset_log_file     Reset the log file            */
77 /*                                                                        */
78 /*  CALLED BY                                                             */
79 /*                                                                        */
80 /*    Application Code                                                    */
81 /*                                                                        */
82 /*  RELEASE HISTORY                                                       */
83 /*                                                                        */
84 /*    DATE              NAME                      DESCRIPTION             */
85 /*                                                                        */
86 /*  05-19-2020     William E. Lamie         Initial Version 6.0           */
87 /*  09-30-2020     William E. Lamie         Modified comment(s),          */
88 /*                                            resulting in version 6.1    */
89 /*                                                                        */
90 /**************************************************************************/
_fx_directory_rename(FX_MEDIA * media_ptr,CHAR * old_directory_name,CHAR * new_directory_name)91 UINT  _fx_directory_rename(FX_MEDIA *media_ptr, CHAR *old_directory_name, CHAR *new_directory_name)
92 {
93 
94 UINT         status;
95 FX_DIR_ENTRY old_dir_entry;
96 FX_DIR_ENTRY new_dir_entry;
97 FX_DIR_ENTRY search_directory;
98 CHAR        *new_name_ptr;
99 ULONG        i;
100 CHAR        *work_ptr;
101 CHAR         alpha, beta;
102 #ifdef FX_RENAME_PATH_INHERIT
103 UINT         j;
104 #endif
105 
106 
107 #ifndef FX_MEDIA_STATISTICS_DISABLE
108 
109     /* Increment the number of times this service has been called.  */
110     media_ptr -> fx_media_directory_renames++;
111 #endif
112 
113     /* Setup pointers to media name buffers.  */
114     old_dir_entry.fx_dir_entry_name =     media_ptr -> fx_media_name_buffer + FX_MAX_LONG_NAME_LEN;
115     new_dir_entry.fx_dir_entry_name =     media_ptr -> fx_media_name_buffer + FX_MAX_LONG_NAME_LEN * 2;
116     search_directory.fx_dir_entry_name =  media_ptr -> fx_media_name_buffer + FX_MAX_LONG_NAME_LEN * 3;
117 
118     /* Clear the short name strings.  */
119     old_dir_entry.fx_dir_entry_short_name[0] =     0;
120     new_dir_entry.fx_dir_entry_short_name[0] =     0;
121     search_directory.fx_dir_entry_short_name[0] =  0;
122 
123     /* Determine if the supplied name is less than the maximum supported name size. The
124        maximum name (FX_MAX_LONG_NAME_LEN) is defined in fx_api.h.  */
125     i =  0;
126     work_ptr =  (CHAR *)new_directory_name;
127     while (*work_ptr && (i < FX_MAX_LONG_NAME_LEN))
128     {
129 
130         /* Determine if the character designates a new path.  */
131         if ((*work_ptr == '\\') || (*work_ptr == '/'))
132         {
133             /* Yes, reset the name size.  */
134             i =  0;
135         }
136         /* Check for leading spaces.  */
137         else if ((*work_ptr != ' ') || (i != 0))
138         {
139 
140             /* No leading spaces, increment the name size.  */
141             i++;
142         }
143 
144         /* Move to the next character.  */
145         work_ptr++;
146     }
147 
148     /* Determine if the supplied name is valid.  */
149     if ((i == 0) || (i >= FX_MAX_LONG_NAME_LEN))
150     {
151 
152         /* Return an invalid name value.  */
153         return(FX_INVALID_NAME);
154     }
155 
156     /* Check the media to make sure it is open.  */
157     if (media_ptr -> fx_media_id != FX_MEDIA_ID)
158     {
159 
160         /* Return the media not opened error.  */
161         return(FX_MEDIA_NOT_OPEN);
162     }
163 
164     /* If trace is enabled, insert this event into the trace buffer.  */
165     FX_TRACE_IN_LINE_INSERT(FX_TRACE_DIRECTORY_RENAME, media_ptr, old_directory_name, new_directory_name, 0, FX_TRACE_DIRECTORY_EVENTS, 0, 0)
166 
167     /* Protect against other threads accessing the media.  */
168     FX_PROTECT
169 
170 #ifdef FX_ENABLE_FAULT_TOLERANT
171     /* Start transaction. */
172     _fx_fault_tolerant_transaction_start(media_ptr);
173 #endif /* FX_ENABLE_FAULT_TOLERANT */
174 
175     /* Check for write protect at the media level (set by driver).  */
176     if (media_ptr -> fx_media_driver_write_protect)
177     {
178 
179 #ifdef FX_ENABLE_FAULT_TOLERANT
180         FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr);
181 #endif /* FX_ENABLE_FAULT_TOLERANT */
182 
183         /* Release media protection.  */
184         FX_UNPROTECT
185 
186         /* Return write protect error.  */
187         return(FX_WRITE_PROTECT);
188     }
189 
190     /* Search the system for the supplied directory name.  */
191     status =  _fx_directory_search(media_ptr, old_directory_name, &old_dir_entry, &search_directory, FX_NULL);
192 
193     /* Determine if the search was successful.  */
194     if (status != FX_SUCCESS)
195     {
196 
197 #ifdef FX_ENABLE_FAULT_TOLERANT
198         FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr);
199 #endif /* FX_ENABLE_FAULT_TOLERANT */
200 
201         /* Release media protection.  */
202         FX_UNPROTECT
203 
204         /* Return the error code.  */
205         return(status);
206     }
207 
208     /* Check to make sure the found entry is a directory.  */
209     if (!(old_dir_entry.fx_dir_entry_attributes & (UCHAR)(FX_DIRECTORY)))
210     {
211 
212 #ifdef FX_ENABLE_FAULT_TOLERANT
213         FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr);
214 #endif /* FX_ENABLE_FAULT_TOLERANT */
215 
216         /* Release media protection.  */
217         FX_UNPROTECT
218 
219         /* Return the not a directory error code.  */
220         return(FX_NOT_DIRECTORY);
221     }
222 
223 #ifdef FX_RENAME_PATH_INHERIT
224 
225     /* Determine if the source directory name has a path and the target directory name does not.  */
226     if (((old_directory_name[0] == '/') || (old_directory_name[0] == '\\')) && (new_directory_name[0] != '/') && (new_directory_name[0] != '\\'))
227     {
228 
229         /* In this case, we need to prepend the path of the old directory name to that of the new directory name.  */
230 
231         /* Setup pointer to the rename buffer.  */
232         work_ptr =  (CHAR *)media_ptr -> fx_media_rename_buffer;
233 
234         /* First, copy the path of the old directory name.  */
235         i =  0;
236         j =  0;
237         while ((old_directory_name[i]) && (i < FX_MAXIMUM_PATH))
238         {
239 
240             /* Copy a character into the rename buffer.  */
241             *work_ptr++ =  old_directory_name[i];
242 
243             /* Determine if this character is directory separator.  */
244             if ((old_directory_name[i] == '/') || (old_directory_name[i] == '\\'))
245             {
246 
247                 /* Yes, directory separator has been found - remember the index.  */
248                 j =  i;
249             }
250 
251             /* Move to next position in the old directory name.  */
252             i++;
253         }
254 
255         /* At this point, we have the path stored in the rename buffer.  */
256 
257         /* Position past the last slash or backslash.  */
258         j++;
259 
260         /* Reset the working pointer to the position after the last directory separator.  */
261         work_ptr =  (CHAR *)&(media_ptr -> fx_media_rename_buffer[j]);
262 
263         /* Now copy the new directory name into the rename buffer.  */
264         i =  0;
265         while ((new_directory_name[i]) && (j < FX_MAXIMUM_PATH))
266         {
267 
268             /* Copy a character into the rename buffer.  */
269             *work_ptr++ =  new_directory_name[i];
270 
271             /* Move to next character.  */
272             i++;
273             j++;
274         }
275 
276         /* Determine if the path was successfully prepended.  */
277         if (new_directory_name[i])
278         {
279 
280             /* No, there was not enough room in the destination buffer.  */
281 
282 #ifdef FX_ENABLE_FAULT_TOLERANT
283             FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr);
284 #endif /* FX_ENABLE_FAULT_TOLERANT */
285 
286             /* Release media protection.  */
287             FX_UNPROTECT
288 
289             /* Return the invalid path error code.  */
290             return(FX_INVALID_PATH);
291         }
292 
293         /* Place a NULL at the end of the string.  */
294         *work_ptr =  (CHAR)FX_NULL;
295 
296         /* At this point, we have successfully prepended the path in the new directory name, override
297            the new directory name so it is used from now on.  */
298         new_directory_name =  (CHAR *)media_ptr -> fx_media_rename_buffer;
299     }
300 #endif
301 
302     /* Search the media for the new directory name - including any supplied path.  */
303     status = _fx_directory_search(media_ptr, new_directory_name, &new_dir_entry, &search_directory, &new_name_ptr);
304 
305     /* Determine if the search found anything.  */
306     if (status == FX_SUCCESS)
307     {
308 
309         /* Determine if the new name simply has an ASCII case change. If so, simply let the processing
310            continue.  */
311         i =  0;
312         do
313         {
314 
315             /* Pickup an old name and new name character and convert to upper case if necessary.  */
316             alpha =  old_directory_name[i];
317             if ((alpha >= 'a') && (alpha <= 'z'))
318             {
319 
320                 /* Lower case, convert to upper case!  */
321                 alpha =  (CHAR)((INT)alpha - 0x20);
322             }
323             beta =   new_directory_name[i];
324             if ((beta >= 'a') && (beta <= 'z'))
325             {
326 
327                 /* Lower case, convert to upper case!  */
328                 beta =  (CHAR)((INT)beta - 0x20);
329             }
330 
331             /* Now compare the characters.  */
332             if ((alpha != beta) || (alpha == 0))
333             {
334 
335                 /* Get out of this loop!  */
336                 break;
337             }
338 
339             /* Move to next character.  */
340             i++;
341         } while (i < (FX_MAXIMUM_PATH-1));
342 
343         /* Now determine if the names match.  */
344         if (alpha != beta)
345         {
346 
347             /* Yes, the directory name already exists in the target location.  */
348 
349 #ifdef FX_ENABLE_FAULT_TOLERANT
350             FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr);
351 #endif /* FX_ENABLE_FAULT_TOLERANT */
352 
353             /* Release media protection.  */
354             FX_UNPROTECT
355 
356             /* Return the error code.  */
357             return(FX_ALREADY_CREATED);
358         }
359     }
360 
361     /* Make sure the name is valid.  */
362     if (_fx_directory_name_extract(new_name_ptr, &new_dir_entry.fx_dir_entry_name[0]))
363     {
364 
365 #ifdef FX_ENABLE_FAULT_TOLERANT
366         FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr);
367 #endif /* FX_ENABLE_FAULT_TOLERANT */
368 
369         /* Release media protection */
370         FX_UNPROTECT
371 
372         /* Return the error code */
373         return(FX_INVALID_NAME);
374     }
375 
376     /* Look for a free slot in the target directory.  */
377     status =  _fx_directory_free_search(media_ptr, &search_directory, &new_dir_entry);
378 
379     /* Was a free slot found?  */
380     if (status != FX_SUCCESS)
381     {
382 
383 #ifdef FX_ENABLE_FAULT_TOLERANT
384         FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr);
385 #endif /* FX_ENABLE_FAULT_TOLERANT */
386 
387         /* No, release protection.  */
388         FX_UNPROTECT
389 
390         /* Return the error code.  */
391         return(status);
392     }
393 
394     /* Extract the new directory name.  */
395     _fx_directory_name_extract(new_name_ptr, &new_dir_entry.fx_dir_entry_name[0]);
396 
397     /* Determine if the name was a long directory name.   */
398     if (new_dir_entry.fx_dir_entry_long_name_present)
399     {
400 
401         /* Yes, clear the short directory name to force a new one.  */
402         new_dir_entry.fx_dir_entry_short_name[0] =  0;
403     }
404 
405     /* Setup new attributes for the new directory entry.  */
406     new_dir_entry.fx_dir_entry_attributes = old_dir_entry.fx_dir_entry_attributes;
407     new_dir_entry.fx_dir_entry_cluster    = old_dir_entry.fx_dir_entry_cluster;
408     new_dir_entry.fx_dir_entry_file_size  = old_dir_entry.fx_dir_entry_file_size;
409 
410     /* Save the reserved field.  */
411     new_dir_entry.fx_dir_entry_reserved =            old_dir_entry.fx_dir_entry_reserved;
412 
413     /* Set time and date stamps.  */
414     new_dir_entry.fx_dir_entry_created_time_ms =     old_dir_entry.fx_dir_entry_created_time_ms;
415     new_dir_entry.fx_dir_entry_created_time =        old_dir_entry.fx_dir_entry_created_time;
416     new_dir_entry.fx_dir_entry_created_date =        old_dir_entry.fx_dir_entry_created_date;
417     new_dir_entry.fx_dir_entry_last_accessed_date =  old_dir_entry.fx_dir_entry_last_accessed_date;
418     new_dir_entry.fx_dir_entry_time =                old_dir_entry.fx_dir_entry_time;
419     new_dir_entry.fx_dir_entry_date =                old_dir_entry.fx_dir_entry_date;
420 
421 
422     /* Is there a leading dot?  */
423     if (new_dir_entry.fx_dir_entry_name[0] == '.')
424     {
425 
426         /* Yes, toggle the hidden attribute bit.  */
427         new_dir_entry.fx_dir_entry_attributes |=  FX_HIDDEN;
428     }
429 
430 #ifndef FX_MEDIA_DISABLE_SEARCH_CACHE
431 
432     /* Invalidate the directory cache.  */
433     media_ptr -> fx_media_last_found_name[0] =  FX_NULL;
434 #endif
435 
436     /* Now write out the directory entry.  */
437     status =  _fx_directory_entry_write(media_ptr, &new_dir_entry);
438 
439     /* Determine if the write was successful.  */
440     if (status != FX_SUCCESS)
441     {
442 
443 #ifdef FX_ENABLE_FAULT_TOLERANT
444         FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr);
445 #endif /* FX_ENABLE_FAULT_TOLERANT */
446 
447         /* Release media protection.  */
448         FX_UNPROTECT
449 
450         /* Return the error code.  */
451         return(status);
452     }
453 
454     /* Set the old directory entry to free.  */
455     old_dir_entry.fx_dir_entry_name[0] =        (CHAR)FX_DIR_ENTRY_FREE;
456     old_dir_entry.fx_dir_entry_short_name[0] =  (CHAR)FX_DIR_ENTRY_FREE;
457 
458     /* Now wipe out the old directory entry.  */
459     status =  _fx_directory_entry_write(media_ptr, &old_dir_entry);
460 
461 #ifdef FX_ENABLE_FAULT_TOLERANT
462     /* Check for a bad status.  */
463     if (status != FX_SUCCESS)
464     {
465 
466         FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr);
467 
468         /* Release media protection.  */
469         FX_UNPROTECT
470 
471         /* Return the bad status.  */
472         return(status);
473     }
474 
475     /* End transaction. */
476     status = _fx_fault_tolerant_transaction_end(media_ptr);
477 #endif /* FX_ENABLE_FAULT_TOLERANT */
478 
479     /* Release media protection.  */
480     FX_UNPROTECT
481 
482     /* Directory rename is complete, return status.  */
483     return(status);
484 }
485 
486