SolidSyslog coverage report
Current view: top level - Platform/FatFs/Source - SolidSyslogFatFsFile.c (source / functions) Coverage Total Hit
Test: SolidSyslog Lines: 100.0 % 103 103
Test Date: 2026-11-12 23:14:35 Functions: 100.0 % 19 19
Branches: 100.0 % 48 48

             Branch data     Line data    Source code
       1                 :             : /* SPDX-FileCopyrightText: Copyright 2026 Cozens Software Solutions Limited
       2                 :             :  * SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0 OR LicenseRef-PolyForm-Internal-Use-1.0.0 OR LicenseRef-COSOSO-Commercial
       3                 :             :  */
       4                 :             : 
       5                 :             : #include "SolidSyslogFatFsFile.h"
       6                 :             : 
       7                 :             : #include <stdbool.h>
       8                 :             : #include <stddef.h>
       9                 :             : #include <stdint.h>
      10                 :             : 
      11                 :             : #include "SolidSyslogError.h"
      12                 :             : #include "SolidSyslogErrorCategory.h"
      13                 :             : #include "SolidSyslogFatFsFileErrors.h"
      14                 :             : #include "SolidSyslogFatFsFilePrivate.h"
      15                 :             : #include "SolidSyslogFileCategories.h"
      16                 :             : #include "SolidSyslogFileDefinition.h"
      17                 :             : #include "SolidSyslogNullFile.h"
      18                 :             : #include "ff.h"
      19                 :             : 
      20                 :             : const struct SolidSyslogErrorSource SolidSyslogFatFsFileErrorSource = {"FatFsFile", SolidSyslogFileErrors_Name};
      21                 :             : 
      22                 :             : #define FATFSFILE_READ_WRITE_OR_CREATE (FA_READ | FA_WRITE | FA_OPEN_ALWAYS)
      23                 :             : 
      24                 :             : static bool FatFsFile_Open(struct SolidSyslogFile* base, const char* path);
      25                 :             : static void FatFsFile_Close(struct SolidSyslogFile* base);
      26                 :             : static bool FatFsFile_IsOpen(struct SolidSyslogFile* base);
      27                 :             : static bool FatFsFile_Read(struct SolidSyslogFile* base, void* buf, size_t count);
      28                 :             : static bool FatFsFile_Write(struct SolidSyslogFile* base, const void* buf, size_t count);
      29                 :             : static void FatFsFile_SeekTo(struct SolidSyslogFile* base, size_t offset);
      30                 :             : static bool FatFsFile_Size(struct SolidSyslogFile* base, size_t* size);
      31                 :             : static void FatFsFile_Truncate(struct SolidSyslogFile* base);
      32                 :             : static bool FatFsFile_Exists(struct SolidSyslogFile* base, const char* path);
      33                 :             : static bool FatFsFile_Delete(struct SolidSyslogFile* base, const char* path);
      34                 :             : 
      35                 :             : static inline struct SolidSyslogFatFsFile* FatFsFile_SelfFromBase(struct SolidSyslogFile* base);
      36                 :             : static inline FIL* FatFsFile_Handle(struct SolidSyslogFile* base);
      37                 :             : static inline void FatFsFile_ReportFailure(enum SolidSyslogFileErrors code, FRESULT result);
      38                 :             : static inline bool FatFsFile_Sync(struct SolidSyslogFile* base);
      39                 :             : static inline bool FatFsFile_IsCloseStillOwed(FRESULT result);
      40                 :             : static bool FatFsFile_SettleOwedClose(struct SolidSyslogFatFsFile* self);
      41                 :             : static inline bool FatFsFile_IsRemovedOrAbsent(FRESULT result);
      42                 :             : 
      43                 :          60 : void SolidSyslogFatFsFile_Initialise(struct SolidSyslogFile* base)
      44                 :             : {
      45                 :          60 :     struct SolidSyslogFatFsFile* self = FatFsFile_SelfFromBase(base);
      46                 :          60 :     self->Base.Open = FatFsFile_Open;
      47                 :          60 :     self->Base.Close = FatFsFile_Close;
      48                 :          60 :     self->Base.IsOpen = FatFsFile_IsOpen;
      49                 :          60 :     self->Base.Read = FatFsFile_Read;
      50                 :          60 :     self->Base.Write = FatFsFile_Write;
      51                 :          60 :     self->Base.SeekTo = FatFsFile_SeekTo;
      52                 :          60 :     self->Base.Size = FatFsFile_Size;
      53                 :          60 :     self->Base.Truncate = FatFsFile_Truncate;
      54                 :          60 :     self->Base.Exists = FatFsFile_Exists;
      55                 :          60 :     self->Base.Delete = FatFsFile_Delete;
      56                 :          60 :     self->IsOpen = false;
      57                 :          60 :     self->CloseOwed = false;
      58                 :          60 : }
      59                 :             : 
      60                 :         254 : static inline struct SolidSyslogFatFsFile* FatFsFile_SelfFromBase(struct SolidSyslogFile* base)
      61                 :             : {
      62                 :         254 :     return (struct SolidSyslogFatFsFile*) base;
      63                 :             : }
      64                 :             : 
      65                 :             : /* A close still owed is tried once more; the slot is given back whatever it
      66                 :             :  * answers. */
      67                 :          60 : void SolidSyslogFatFsFile_Cleanup(struct SolidSyslogFile* base)
      68                 :             : {
      69                 :          60 :     struct SolidSyslogFatFsFile* self = FatFsFile_SelfFromBase(base);
      70         [ +  + ]:          60 :     if (self->IsOpen)
      71                 :             :     {
      72                 :          31 :         FatFsFile_Close(base);
      73                 :             :     }
      74         [ +  + ]:          29 :     else if (self->CloseOwed)
      75                 :             :     {
      76                 :           6 :         (void) FatFsFile_SettleOwedClose(self);
      77                 :             :     }
      78                 :             :     else
      79                 :             :     {
      80                 :             :         /* closed, and nothing owed */
      81                 :             :     }
      82                 :          60 :     self->CloseOwed = false;
      83                 :             :     /* Overwrite the abstract base with the shared NullFile vtable so
      84                 :             :      * use-after-destroy is a safe no-op rather than a NULL-fn-pointer crash. */
      85                 :          60 :     *base = *SolidSyslogNullFile_Get();
      86                 :          60 : }
      87                 :             : 
      88                 :          45 : static void FatFsFile_Close(struct SolidSyslogFile* base)
      89                 :             : {
      90                 :          45 :     struct SolidSyslogFatFsFile* self = FatFsFile_SelfFromBase(base);
      91         [ +  + ]:          45 :     if (self->IsOpen)
      92                 :             :     {
      93                 :          43 :         FRESULT result = f_close(&self->Fp);
      94                 :             :         /* Closed, as the contract says, whatever f_close answers. A failed
      95                 :             :          * sync leaves FatFs holding the file object, so the close is owed. */
      96                 :          43 :         self->IsOpen = false;
      97                 :          43 :         self->CloseOwed = FatFsFile_IsCloseStillOwed(result);
      98         [ +  + ]:          43 :         if (result != FR_OK)
      99                 :             :         {
     100                 :          13 :             FatFsFile_ReportFailure(SOLIDSYSLOG_FILE_ERROR_CLOSE_FAILED, result);
     101                 :             :         }
     102                 :             :     }
     103                 :          45 : }
     104                 :             : 
     105                 :             : /* FR_INVALID_OBJECT is FatFs saying it no longer knows the file object - a
     106                 :             :  * remount discards every one, and its locks with them - so there is nothing
     107                 :             :  * left for a later f_close to release. */
     108                 :          55 : static inline bool FatFsFile_IsCloseStillOwed(FRESULT result)
     109                 :             : {
     110   [ +  +  +  + ]:          55 :     return (result != FR_OK) && (result != FR_INVALID_OBJECT);
     111                 :             : }
     112                 :             : 
     113                 :             : /* The fault, with the FRESULT behind it when the call answered one. A short
     114                 :             :  * transfer that FatFs answers FR_OK leaves no code. */
     115                 :          36 : static inline void FatFsFile_ReportFailure(enum SolidSyslogFileErrors code, FRESULT result)
     116                 :             : {
     117         [ +  + ]:          36 :     if (result != FR_OK)
     118                 :             :     {
     119                 :          34 :         SolidSyslog_ErrorWithNativeCode(
     120                 :             :             SOLIDSYSLOG_FILE_IO_FAILED_SEVERITY,
     121                 :             :             &SolidSyslogFatFsFileErrorSource,
     122                 :             :             SOLIDSYSLOG_CAT_FILE_IO_FAILED,
     123                 :             :             (int32_t) code,
     124                 :             :             (int32_t) result
     125                 :             :         );
     126                 :             :     }
     127                 :             :     else
     128                 :             :     {
     129                 :           2 :         FatFsFile_Report(SOLIDSYSLOG_FILE_IO_FAILED_SEVERITY, SOLIDSYSLOG_CAT_FILE_IO_FAILED, code);
     130                 :             :     }
     131                 :          36 : }
     132                 :             : 
     133                 :          48 : static bool FatFsFile_Open(struct SolidSyslogFile* base, const char* path)
     134                 :             : {
     135                 :          48 :     struct SolidSyslogFatFsFile* self = FatFsFile_SelfFromBase(base);
     136                 :          48 :     bool opened = false;
     137                 :             :     /* An owed close is settled before the file object is reused: f_open would
     138                 :             :      * discard what FatFs still holds in it, and its FF_FS_LOCK entry with it.
     139                 :             :      * One that cannot be settled refuses the open, and the file stays closed
     140                 :             :      * with the close still owed for the next one. */
     141         [ +  + ]:          48 :     if (FatFsFile_SettleOwedClose(self))
     142                 :             :     {
     143                 :          45 :         FRESULT result = f_open(&self->Fp, path, FATFSFILE_READ_WRITE_OR_CREATE);
     144                 :          45 :         self->IsOpen = (result == FR_OK);
     145         [ +  + ]:          45 :         if (!self->IsOpen)
     146                 :             :         {
     147                 :           2 :             FatFsFile_ReportFailure(SOLIDSYSLOG_FILE_ERROR_OPEN_FAILED, result);
     148                 :             :         }
     149                 :          45 :         opened = self->IsOpen;
     150                 :             :     }
     151                 :          48 :     return opened;
     152                 :             : }
     153                 :             : 
     154                 :             : /* True when nothing is owed, or the owed close has now gone through. A retry
     155                 :             :  * that fails again is reported again. */
     156                 :          54 : static bool FatFsFile_SettleOwedClose(struct SolidSyslogFatFsFile* self)
     157                 :             : {
     158         [ +  + ]:          54 :     if (self->CloseOwed)
     159                 :             :     {
     160                 :          12 :         FRESULT result = f_close(&self->Fp);
     161                 :          12 :         self->CloseOwed = FatFsFile_IsCloseStillOwed(result);
     162         [ +  + ]:          12 :         if (self->CloseOwed)
     163                 :             :         {
     164                 :           9 :             FatFsFile_ReportFailure(SOLIDSYSLOG_FILE_ERROR_CLOSE_FAILED, result);
     165                 :             :         }
     166                 :             :     }
     167                 :          54 :     return !self->CloseOwed;
     168                 :             : }
     169                 :             : 
     170                 :           6 : static bool FatFsFile_IsOpen(struct SolidSyslogFile* base)
     171                 :             : {
     172                 :           6 :     return FatFsFile_SelfFromBase(base)->IsOpen;
     173                 :             : }
     174                 :             : 
     175                 :           7 : static bool FatFsFile_Read(struct SolidSyslogFile* base, void* buf, size_t count)
     176                 :             : {
     177                 :           7 :     UINT bytesRead = 0U;
     178                 :           7 :     FRESULT result = f_read(FatFsFile_Handle(base), buf, (UINT) count, &bytesRead);
     179                 :             :     /* A short read is FR_OK with fewer bytes; the store judges it. */
     180         [ +  + ]:           7 :     if (result != FR_OK)
     181                 :             :     {
     182                 :           2 :         FatFsFile_ReportFailure(SOLIDSYSLOG_FILE_ERROR_READ_FAILED, result);
     183                 :             :     }
     184   [ +  +  +  + ]:           7 :     return (result == FR_OK) && (bytesRead == count);
     185                 :             : }
     186                 :             : 
     187                 :          35 : static inline FIL* FatFsFile_Handle(struct SolidSyslogFile* base)
     188                 :             : {
     189                 :          35 :     return &FatFsFile_SelfFromBase(base)->Fp;
     190                 :             : }
     191                 :             : 
     192                 :          10 : static bool FatFsFile_Write(struct SolidSyslogFile* base, const void* buf, size_t count)
     193                 :             : {
     194                 :          10 :     UINT bytesWritten = 0U;
     195                 :          10 :     FRESULT result = f_write(FatFsFile_Handle(base), buf, (UINT) count, &bytesWritten);
     196                 :             :     /* A full volume is FR_OK with fewer bytes: a failed write with no code. */
     197   [ +  +  +  + ]:          10 :     bool wroteAllData = (result == FR_OK) && (bytesWritten == count);
     198         [ +  + ]:          10 :     if (wroteAllData)
     199                 :             :     {
     200                 :           5 :         wroteAllData = FatFsFile_Sync(base);
     201                 :             :     }
     202                 :             :     else
     203                 :             :     {
     204                 :           5 :         FatFsFile_ReportFailure(SOLIDSYSLOG_FILE_ERROR_WRITE_FAILED, result);
     205                 :             :     }
     206                 :          10 :     return wroteAllData;
     207                 :             : }
     208                 :             : 
     209                 :           5 : static inline bool FatFsFile_Sync(struct SolidSyslogFile* base)
     210                 :             : {
     211                 :           5 :     FRESULT result = f_sync(FatFsFile_Handle(base));
     212         [ +  + ]:           5 :     if (result != FR_OK)
     213                 :             :     {
     214                 :           2 :         FatFsFile_ReportFailure(SOLIDSYSLOG_FILE_ERROR_SYNC_FAILED, result);
     215                 :             :     }
     216                 :           5 :     return result == FR_OK;
     217                 :             : }
     218                 :             : 
     219                 :           4 : static void FatFsFile_SeekTo(struct SolidSyslogFile* base, size_t offset)
     220                 :             : {
     221                 :           4 :     FRESULT result = f_lseek(FatFsFile_Handle(base), (FSIZE_t) offset);
     222         [ +  + ]:           4 :     if (result != FR_OK)
     223                 :             :     {
     224                 :           1 :         FatFsFile_ReportFailure(SOLIDSYSLOG_FILE_ERROR_SEEK_FAILED, result);
     225                 :             :     }
     226                 :           4 : }
     227                 :             : 
     228                 :           2 : static bool FatFsFile_Size(struct SolidSyslogFile* base, size_t* size)
     229                 :             : {
     230                 :             :     /* f_size reads the file object's own record of its length and cannot fail. */
     231                 :           2 :     *size = (size_t) f_size(FatFsFile_Handle(base));
     232                 :           2 :     return true;
     233                 :             : }
     234                 :             : 
     235                 :             : /* f_truncate cuts the file at its current position, so the seek to the start
     236                 :             :  * is part of the truncate: a seek that fails leaves the file where it was,
     237                 :             :  * and is reported as the truncate failing. */
     238                 :           4 : static void FatFsFile_Truncate(struct SolidSyslogFile* base)
     239                 :             : {
     240                 :           4 :     FRESULT result = f_lseek(FatFsFile_Handle(base), 0);
     241         [ +  + ]:           4 :     if (result == FR_OK)
     242                 :             :     {
     243                 :           3 :         result = f_truncate(FatFsFile_Handle(base));
     244                 :             :     }
     245         [ +  + ]:           4 :     if (result != FR_OK)
     246                 :             :     {
     247                 :           2 :         FatFsFile_ReportFailure(SOLIDSYSLOG_FILE_ERROR_TRUNCATE_FAILED, result);
     248                 :             :     }
     249                 :           4 : }
     250                 :             : 
     251                 :             : /* A path that is not there is the answer, not a fault, so nothing is reported. */
     252                 :           3 : static bool FatFsFile_Exists(struct SolidSyslogFile* base, const char* path)
     253                 :             : {
     254                 :             :     (void) base;
     255                 :           3 :     return f_stat(path, NULL) == FR_OK;
     256                 :             : }
     257                 :             : 
     258                 :           5 : static bool FatFsFile_Delete(struct SolidSyslogFile* base, const char* path)
     259                 :             : {
     260                 :             :     (void) base;
     261                 :           5 :     return FatFsFile_IsRemovedOrAbsent(f_unlink(path));
     262                 :             : }
     263                 :             : 
     264                 :             : /* The contract counts an absent path as removed, whether the file or its
     265                 :             :  * directory is missing. */
     266                 :           5 : static inline bool FatFsFile_IsRemovedOrAbsent(FRESULT result)
     267                 :             : {
     268   [ +  +  +  +  :           5 :     return (result == FR_OK) || (result == FR_NO_FILE) || (result == FR_NO_PATH);
                   +  + ]
     269                 :             : }
        

Generated by: LCOV version 2.0-1