GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: fx_file_rename.c Lines: 100 100 100.0 %
Date: 2026-03-06 18:49:02 Branches: 62 62 100.0 %

Line Branch Exec Source
1
/***************************************************************************
2
 * Copyright (c) 2024 Microsoft Corporation
3
 * Copyright (c) 2026-present Eclipse ThreadX contributors
4
 *
5
 * This program and the accompanying materials are made available under the
6
 * terms of the MIT License which is available at
7
 * https://opensource.org/licenses/MIT.
8
 *
9
 * SPDX-License-Identifier: MIT
10
 **************************************************************************/
11
12
13
/**************************************************************************/
14
/**************************************************************************/
15
/**                                                                       */
16
/** FileX Component                                                       */
17
/**                                                                       */
18
/**   File                                                                */
19
/**                                                                       */
20
/**************************************************************************/
21
/**************************************************************************/
22
23
#define FX_SOURCE_CODE
24
25
26
/* Include necessary system files.  */
27
28
#include "fx_api.h"
29
#include "fx_system.h"
30
#include "fx_directory.h"
31
#include "fx_file.h"
32
#include "fx_utility.h"
33
#ifdef FX_ENABLE_FAULT_TOLERANT
34
#include "fx_fault_tolerant.h"
35
#endif /* FX_ENABLE_FAULT_TOLERANT */
36
37
38
/**************************************************************************/
39
/*                                                                        */
40
/*  FUNCTION                                               RELEASE        */
41
/*                                                                        */
42
/*    _fx_file_rename                                     PORTABLE C      */
43
/*                                                           6.1          */
44
/*  AUTHOR                                                                */
45
/*                                                                        */
46
/*    William E. Lamie, Microsoft Corporation                             */
47
/*                                                                        */
48
/*  DESCRIPTION                                                           */
49
/*                                                                        */
50
/*    This function first attempts to find the specified file.  If found, */
51
/*    the rename request is valid and the directory entry will be changed */
52
/*    to the new file name.  Otherwise, if the file is not found, the     */
53
/*    appropriate error code is returned to the caller.                   */
54
/*                                                                        */
55
/*  INPUT                                                                 */
56
/*                                                                        */
57
/*    media_ptr                             Media control block pointer   */
58
/*    old_file_name                         Old file name pointer         */
59
/*    new_file_name                         New file name pointer         */
60
/*                                                                        */
61
/*  OUTPUT                                                                */
62
/*                                                                        */
63
/*    return status                                                       */
64
/*                                                                        */
65
/*  CALLS                                                                 */
66
/*                                                                        */
67
/*    _fx_directory_entry_write             Write the new directory entry */
68
/*    _fx_directory_free_search             Search for a free directory   */
69
/*                                            entry in target directory   */
70
/*    _fx_directory_name_extract            Extract the new filename      */
71
/*    _fx_directory_search                  Search for the file name in   */
72
/*                                          the directory structure       */
73
/*    _fx_fault_tolerant_transaction_start  Start fault tolerant          */
74
/*                                            transaction                 */
75
/*    _fx_fault_tolerant_transaction_end    End fault tolerant transaction*/
76
/*    _fx_fault_tolerant_recover            Recover FAT chain             */
77
/*                                                                        */
78
/*  CALLED BY                                                             */
79
/*                                                                        */
80
/*    Application Code                                                    */
81
/*                                                                        */
82
/**************************************************************************/
83
91
UINT  _fx_file_rename(FX_MEDIA *media_ptr, CHAR *old_file_name, CHAR *new_file_name)
84
{
85
86
ULONG        i;
87
CHAR        *work_ptr;
88
CHAR         alpha, beta;
89
UINT         status;
90
91
#ifndef FX_DONT_UPDATE_OPEN_FILES
92
ULONG        open_count;
93
FX_FILE     *search_ptr;
94
#endif
95
CHAR        *new_name_ptr;
96
FX_DIR_ENTRY old_dir_entry, new_dir_entry;
97
FX_DIR_ENTRY search_directory;
98
#ifdef FX_RENAME_PATH_INHERIT
99
UINT         j;
100
#endif
101
UCHAR        not_a_file_attr;
102
103
104
#ifndef FX_MEDIA_STATISTICS_DISABLE
105
106
    /* Increment the number of times this service has been called.  */
107
91
    media_ptr -> fx_media_file_renames++;
108
#endif
109
110
    /* Setup pointers to media name buffers.  */
111
91
    old_dir_entry.fx_dir_entry_name =     media_ptr -> fx_media_name_buffer + FX_MAX_LONG_NAME_LEN;
112
91
    new_dir_entry.fx_dir_entry_name =     media_ptr -> fx_media_name_buffer + FX_MAX_LONG_NAME_LEN * 2;
113
91
    search_directory.fx_dir_entry_name =  media_ptr -> fx_media_name_buffer + FX_MAX_LONG_NAME_LEN * 3;
114
115
    /* Clear the short name strings.  */
116
91
    old_dir_entry.fx_dir_entry_short_name[0] =     0;
117
91
    new_dir_entry.fx_dir_entry_short_name[0] =     0;
118
91
    search_directory.fx_dir_entry_short_name[0] =  0;
119
120
    /* Determine if the supplied name is less than the maximum supported name size. The
121
       maximum name (FX_MAX_LONG_NAME_LEN) is defined in fx_api.h.  */
122
91
    i =  0;
123
91
    work_ptr =  (CHAR *)new_file_name;
124
1845
    while (*work_ptr)
125
    {
126
127
        /* Determine if the character designates a new path.  */
128

1754
        if ((*work_ptr == '\\') || (*work_ptr == '/'))
129
        {
130
            /* Yes, reset the name size.  */
131
10
            i =  0;
132
        }
133
        /* Check for leading spaces.  */
134

1744
        else if ((*work_ptr != ' ') || (i != 0))
135
        {
136
137
            /* No leading spaces, increment the name size.  */
138
1740
            i++;
139
        }
140
141
        /* Move to the next character.  */
142
1754
        work_ptr++;
143
    }
144
145
    /* Determine if the supplied name is valid.  */
146

91
    if ((i == 0) || (i >= FX_MAX_LONG_NAME_LEN))
147
    {
148
149
        /* Return an invalid name value.  */
150
2
        return(FX_INVALID_NAME);
151
    }
152
153
    /* Check the media to make sure it is open.  */
154
89
    if (media_ptr -> fx_media_id != FX_MEDIA_ID)
155
    {
156
157
        /* Return the media not opened error.  */
158
1
        return(FX_MEDIA_NOT_OPEN);
159
    }
160
161
    /* If trace is enabled, insert this event into the trace buffer.  */
162
    FX_TRACE_IN_LINE_INSERT(FX_TRACE_FILE_RENAME, media_ptr, old_file_name, new_file_name, 0, FX_TRACE_FILE_EVENTS, 0, 0)
163
164
    /* Protect against other threads accessing the media.  */
165
88
    FX_PROTECT
166
167
#ifdef FX_ENABLE_FAULT_TOLERANT
168
    /* Start transaction. */
169
    _fx_fault_tolerant_transaction_start(media_ptr);
170
#endif /* FX_ENABLE_FAULT_TOLERANT */
171
172
    /* Check for write protect at the media level (set by driver).  */
173
88
    if (media_ptr -> fx_media_driver_write_protect)
174
    {
175
176
#ifdef FX_ENABLE_FAULT_TOLERANT
177
        FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr);
178
#endif /* FX_ENABLE_FAULT_TOLERANT */
179
180
        /* Release media protection.  */
181
1
        FX_UNPROTECT
182
183
        /* Return write protect error.  */
184
1
        return(FX_WRITE_PROTECT);
185
    }
186
187
    /* Search the system for the supplied file name.  */
188
87
    status =  _fx_directory_search(media_ptr, old_file_name, &old_dir_entry, &search_directory, FX_NULL);
189
190
    /* Determine if the search was successful.  */
191
87
    if (status != FX_SUCCESS)
192
    {
193
194
#ifdef FX_ENABLE_FAULT_TOLERANT
195
        FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr);
196
#endif /* FX_ENABLE_FAULT_TOLERANT */
197
198
        /* Release media protection.  */
199
1
        FX_UNPROTECT
200
201
        /* Return the error code.  */
202
1
        return(status);
203
    }
204
205
86
    not_a_file_attr = FX_DIRECTORY | FX_VOLUME;
206
207
    /* Check to make sure the found entry is a file.  */
208
86
    if (old_dir_entry.fx_dir_entry_attributes & not_a_file_attr)
209
    {
210
211
#ifdef FX_ENABLE_FAULT_TOLERANT
212
        FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr);
213
#endif /* FX_ENABLE_FAULT_TOLERANT */
214
215
        /* Release media protection.  */
216
1
        FX_UNPROTECT
217
218
        /* Return the not a file error code.  */
219
1
        return(FX_NOT_A_FILE);
220
    }
221
222
#ifdef FX_RENAME_PATH_INHERIT
223
224
    /* Determine if the source file name has a path and the target file name does not.  */
225
    if (((old_file_name[0] == '/') || (old_file_name[0] == '\\')) && (new_file_name[0] != '/') && (new_file_name[0] != '\\'))
226
    {
227
228
        /* In this case, we need to prepend the path of the old file name to that of the new file name.  */
229
230
        /* Setup pointer to the rename buffer.  */
231
        work_ptr =  (CHAR *)media_ptr -> fx_media_rename_buffer;
232
233
        /* First, copy the path of the old file name.  */
234
        i =  0;
235
        j =  0;
236
        while ((old_file_name[i]) && (i < FX_MAXIMUM_PATH))
237
        {
238
239
            /* Copy a character into the rename buffer.  */
240
            *work_ptr++ =  old_file_name[i];
241
242
            /* Determine if this character is directory separator.  */
243
            if ((old_file_name[i] == '/') || (old_file_name[i] == '\\'))
244
            {
245
246
                /* Yes, directory separator has been found - remember the index.  */
247
                j =  i;
248
            }
249
250
            /* Move to next position in the old file name.  */
251
            i++;
252
        }
253
254
        /* At this point, we have the path stored in the rename buffer.  */
255
256
        /* Position past the last slash or backslash.  */
257
        j++;
258
259
        /* Reset the working pointer to the position after the last directory separator.  */
260
        work_ptr =  (CHAR *)&(media_ptr -> fx_media_rename_buffer[j]);
261
262
        /* Now copy the new file name into the rename buffer.  */
263
        i =  0;
264
        while ((new_file_name[i]) && (j < FX_MAXIMUM_PATH))
265
        {
266
267
            /* Copy a character into the rename buffer.  */
268
            *work_ptr++ =  new_file_name[i];
269
270
            /* Move to next character.  */
271
            i++;
272
            j++;
273
        }
274
275
        /* Determine if the path was successfully prepended.  */
276
        if (new_file_name[i])
277
        {
278
279
            /* No, there was not enough room in the destination buffer.  */
280
281
#ifdef FX_ENABLE_FAULT_TOLERANT
282
            FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr);
283
#endif /* FX_ENABLE_FAULT_TOLERANT */
284
285
            /* Release media protection.  */
286
            FX_UNPROTECT
287
288
            /* Return the invalid path error code.  */
289
            return(FX_INVALID_PATH);
290
        }
291
292
        /* Place a NULL at the end of the string.  */
293
        *work_ptr =  (CHAR)FX_NULL;
294
295
        /* At this point, we have successfully prepended the path in the new file name, override
296
           the new file name so it is used from now on.  */
297
        new_file_name =  (CHAR *)media_ptr -> fx_media_rename_buffer;
298
    }
299
#endif
300
301
    /* Search the target directory for the same file name.  */
302
85
    status = _fx_directory_search(media_ptr, new_file_name, &new_dir_entry, &search_directory, &new_name_ptr);
303
304
    /* Determine if the name already exists.  */
305
85
    if (status == FX_SUCCESS)
306
    {
307
308
        /* Determine if the new name simply has an ASCII case change. If so, simply let the processing
309
           continue.  */
310
61
        i =  0;
311
        do
312
        {
313
314
            /* Pickup an old name and new name character and convert to upper case if necessary.  */
315
1136
            alpha =  old_file_name[i];
316

1136
            if ((alpha >= 'a') && (alpha <= 'z'))
317
            {
318
319
                /* Lower case, convert to upper case!  */
320
1023
                alpha =  (CHAR)((INT)alpha - 0x20);
321
            }
322
1136
            beta =   new_file_name[i];
323

1136
            if ((beta >= 'a') && (beta <= 'z'))
324
            {
325
326
                /* Lower case, convert to upper case!  */
327
1078
                beta = (CHAR)((INT)beta - 0x20);
328
            }
329
330
            /* Now compare the characters.  */
331

1136
            if ((alpha != beta) || (alpha == 0))
332
            {
333
334
                /* Get out of this loop!  */
335
                break;
336
            }
337
338
            /* Move to next character.  */
339
1079
            i++;
340
1079
        } while (i < (FX_MAXIMUM_PATH-1));
341
342
        /* Now determine if the names match.  */
343
61
        if (alpha != beta)
344
        {
345
346
            /* No, the names do not match so simply return an error
347
               to the caller.  */
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
42
            FX_UNPROTECT
355
356
            /* Return the not a file error code.  */
357
42
            return(FX_ALREADY_CREATED);
358
        }
359
    }
360
361
    /* Change the file name and look for extra stuff at the end.  */
362
43
    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
1
        FX_UNPROTECT
371
372
        /* Invalid name, return error status.  */
373
1
        return(FX_INVALID_NAME);
374
    }
375
376
    /* Search for a free spot in the target directory.  */
377
42
    status = _fx_directory_free_search(media_ptr, &search_directory, &new_dir_entry);
378
379
    /* Determine if a free spot was found.  */
380
42
    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
        /* Release media protection.  */
388
1
        FX_UNPROTECT
389
390
        /* Return the error code.  */
391
1
        return(status);
392
    }
393
394
    /* Extract the new file name.  */
395
41
    _fx_directory_name_extract(new_name_ptr, &new_dir_entry.fx_dir_entry_name[0]);
396
397
    /* Determine if a long name is present.  */
398
41
    if (new_dir_entry.fx_dir_entry_long_name_present)
399
    {
400
401
        /* Yes, clear the short file name.  */
402
35
        new_dir_entry.fx_dir_entry_short_name[0] =  0;
403
    }
404
405
    /* Save the updated directory parameters.  */
406
41
    new_dir_entry.fx_dir_entry_attributes =          old_dir_entry.fx_dir_entry_attributes;
407
41
    new_dir_entry.fx_dir_entry_cluster    =          old_dir_entry.fx_dir_entry_cluster;
408
41
    new_dir_entry.fx_dir_entry_file_size  =          old_dir_entry.fx_dir_entry_file_size;
409
410
    /* Save the reserved field.  */
411
41
    new_dir_entry.fx_dir_entry_reserved =            old_dir_entry.fx_dir_entry_reserved;
412
413
    /* Set time and date stamps.  */
414
41
    new_dir_entry.fx_dir_entry_created_time_ms =     old_dir_entry.fx_dir_entry_created_time_ms;
415
41
    new_dir_entry.fx_dir_entry_created_time =        old_dir_entry.fx_dir_entry_created_time;
416
41
    new_dir_entry.fx_dir_entry_created_date =        old_dir_entry.fx_dir_entry_created_date;
417
41
    new_dir_entry.fx_dir_entry_last_accessed_date =  old_dir_entry.fx_dir_entry_last_accessed_date;
418
41
    new_dir_entry.fx_dir_entry_time =                old_dir_entry.fx_dir_entry_time;
419
41
    new_dir_entry.fx_dir_entry_date =                old_dir_entry.fx_dir_entry_date;
420
421
422
    /* Is there a leading dot?  */
423
41
    if (new_dir_entry.fx_dir_entry_name[0] == '.')
424
    {
425
426
        /* Yes, toggle the hidden attribute bit.  */
427
1
        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
41
    media_ptr -> fx_media_last_found_name[0] =  FX_NULL;
434
#endif
435
436
    /* Now write out the directory entry.  */
437
41
    status =  _fx_directory_entry_write(media_ptr, &new_dir_entry);
438
439
    /* Determine if the write was successful.  */
440
41
    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
1
        FX_UNPROTECT
449
450
        /* Return the error code.  */
451
1
        return(status);
452
    }
453
454
#ifndef FX_DONT_UPDATE_OPEN_FILES
455
456
    /* Search the opened files to update any currently opened files.  */
457
40
    open_count =  media_ptr -> fx_media_opened_file_count;
458
40
    search_ptr =  media_ptr -> fx_media_opened_file_list;
459
49
    while (open_count)
460
    {
461
462
        /* Look at each opened file to see if it matches the file being renamed.  */
463
9
        if ((search_ptr -> fx_file_dir_entry.fx_dir_entry_log_sector ==
464
9
             old_dir_entry.fx_dir_entry_log_sector) &&
465
4
            (search_ptr -> fx_file_dir_entry.fx_dir_entry_byte_offset ==
466
4
             old_dir_entry.fx_dir_entry_byte_offset))
467
        {
468
469
            /* Yes, the file being renamed is already open.  Update the file's
470
               information so that it is kept current.  */
471
3
            search_ptr -> fx_file_dir_entry.fx_dir_entry_cluster =      new_dir_entry.fx_dir_entry_cluster;
472
3
            search_ptr -> fx_file_dir_entry.fx_dir_entry_file_size =    new_dir_entry.fx_dir_entry_file_size;
473
3
            search_ptr -> fx_file_dir_entry.fx_dir_entry_log_sector =   new_dir_entry.fx_dir_entry_log_sector;
474
3
            search_ptr -> fx_file_dir_entry.fx_dir_entry_byte_offset =  new_dir_entry.fx_dir_entry_byte_offset;
475
476
            /* Copy the new name into the file's name buffer.  */
477
3
            i =  0;
478
284
            while (i < (FX_MAX_LONG_NAME_LEN - 1))
479
            {
480
481
                /* Copy byte of the new name.  */
482
283
                search_ptr -> fx_file_dir_entry.fx_dir_entry_name[i] =  new_dir_entry.fx_dir_entry_name[i];
483
484
                /* Move to the next character.  */
485
283
                i++;
486
487
                /* Determine if we are at the end of the name.  */
488
283
                if (new_dir_entry.fx_dir_entry_name[i] == FX_NULL)
489
                {
490
491
                    /* Determine if we are not at the maximum name size.  */
492
3
                    if (i < (FX_MAX_LONG_NAME_LEN - 1))
493
                    {
494
495
                        /* Get out of the loop.   */
496
2
                        break;
497
                    }
498
                }
499
            }
500
501
            /* Set the NULL termination in the copy of the new name.  */
502
3
            search_ptr -> fx_file_dir_entry.fx_dir_entry_name[i] =  FX_NULL;
503
        }
504
505
        /* Adjust the pointer and decrement the search count.  */
506
9
        search_ptr =  search_ptr -> fx_file_opened_next;
507
9
        open_count--;
508
    }
509
#endif
510
511
    /* Now we are ready to remove the old directory entry.  */
512
40
    old_dir_entry.fx_dir_entry_name[0] =        (CHAR)FX_DIR_ENTRY_FREE;
513
40
    old_dir_entry.fx_dir_entry_short_name[0] =  (CHAR)FX_DIR_ENTRY_FREE;
514
515
    /* Now wipe out the old directory entry.  */
516
40
    status =  _fx_directory_entry_write(media_ptr, &old_dir_entry);
517
518
#ifdef FX_ENABLE_FAULT_TOLERANT
519
    /* Check for a bad status.  */
520
    if (status != FX_SUCCESS)
521
    {
522
523
        FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr);
524
525
        /* Release media protection.  */
526
        FX_UNPROTECT
527
528
        /* Return the bad status.  */
529
        return(status);
530
    }
531
532
    /* End transaction. */
533
    status = _fx_fault_tolerant_transaction_end(media_ptr);
534
#endif /* FX_ENABLE_FAULT_TOLERANT */
535
536
    /* Release media protection.  */
537
40
    FX_UNPROTECT
538
539
    /* File rename is complete, return status.  */
540
40
    return(status);
541
}
542