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