/* * Copyright (c) 2013-2019, Huawei Technologies Co., Ltd. All rights reserved. * Copyright (c) 2020, Huawei Device Co., Ltd. All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, * are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, this list of * conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright notice, this list * of conditions and the following disclaimer in the documentation and/or other materials * provided with the distribution. * * 3. Neither the name of the copyright holder nor the names of its contributors may be used * to endorse or promote products derived from this software without specific prior written * permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "ffconf.h" #include "virpartff.h" #include "string.h" #include "diskio.h" #ifdef LOSCFG_FS_FAT_VIRTUAL_PARTITION #if FF_USE_LFN == 0 /* Non-LFN configuration */ #define DEF_NAMBUF #define INIT_NAMBUF(fs) #define FREE_NAMBUF() #else /* LFN configuration */ #if (FF_MAX_LFN < 12) || (FF_MAX_LFN > 255) #error Wrong _MAX_LFN value #endif #if FF_USE_LFN == 1 /* LFN enabled with static working buffer */ static WCHAR g_lfnBuf[FF_MAX_LFN + 1]; /* LFN enabled with static working buffer */ #define DEF_NAMBUF #define INIT_NAMBUF(fs) #define FREE_NAMBUF() #elif FF_USE_LFN == 2 /* LFN enabled with dynamic working buffer on the stack */ #define DEF_NAMBUF WCHAR lbuf[FF_MAX_LFN + 1]; #define INIT_NAMBUF(fs) { (fs)->lfnbuf = lbuf; } #define FREE_NAMBUF() #elif FF_USE_LFN == 3 /* LFN enabled with dynamic working buffer on the heap */ #define DEF_NAMBUF WCHAR *lfn; #define INIT_NAMBUF(fs) { lfn = ff_memalloc((FF_MAX_LFN + 1) * 2); if (!lfn) LEAVE_FF(fs, FR_NOT_ENOUGH_CORE); (fs)->lfnbuf = lfn; } #define FREE_NAMBUF() ff_memfree(lfn) #else #error Wrong FF_USE_LFN setting #endif #endif /* else FF_USE_LFN == 0 */ #if FF_FS_REENTRANT #if FF_USE_LFN == 1 #error Static LFN work area cannot be used at thread-safe configuration #endif #define ENTER_FF(fs) do { if (!lock_fs(fs)) return FR_TIMEOUT; } while (0) #define LEAVE_FF(fs, res) do { unlock_fs(fs, res); return res; } while (0) #else #define ENTER_FF(fs) #define LEAVE_FF(fs, res) return (res) #endif extern FATFS *FatFs[FF_VOLUMES]; /* * follow_virentry: * Compare the top segment with the virtual partition entry and replace it to its CHILD FATFS * * Acceptable return vaule: * - FR_OK : The top segment matches one of the virtual partition entries, and the FATFS * has been replaced to the corresponding FATFS. * - FR_DENIED : The top segment does not matched any of the virtual partition entries, and * the FATFS has kept as it is. Or the virtual partition feature has been * switched down by any reason. * - FR_INT_ERR : Assertion error */ FRESULT follow_virentry(FFOBJID *obj, const TCHAR *path) { TCHAR keyword[FF_MAX_LFN + 1] = {0}; FATFS *fs = obj->fs; INT len; UINT i; (void)memset_s(keyword, sizeof(keyword), 0, sizeof(keyword)); /* Search and copy the first segment in path */ for (len = 0; *path != '/' && *path != '\\' && *path != '\0' && len < FF_MAX_LFN; path++, len++) { keyword[len] = *path; } if (len == 0 || len > _MAX_ENTRYLENGTH) { return FR_DENIED; } /* * Compare the segment does match one for virtual partitions' entry or not, * replace the FATFS if the result is positive */ for (i = 0; i < fs->vir_amount; i++) { if (!CHILDFS(fs, i)) { return FR_INT_ERR; } if (memcmp((CHILDFS(fs, i))->namelabel, keyword, _MAX_ENTRYLENGTH + 1) == 0) { obj->fs = CHILDFS(fs, i); return FR_OK; } } return FR_DENIED; } FRESULT f_checkname(const TCHAR *path) { FRESULT res; DIR dj; FATFS fs; TCHAR *label = (TCHAR *)path; DEF_NAMBUF (void)memset_s(&fs, sizeof(fs), 0, sizeof(fs)); dj.obj.fs = &fs; INIT_NAMBUF(&fs); res = create_name(&dj, &path); /* the last byte of file name can't be a space */ if (res == FR_OK && dj.fn[11] == 0x20) { res = FR_INVALID_NAME; return res; } FREE_NAMBUF(); for (; *label != '\0'; label++) { if (label - path > _MAX_ENTRYLENGTH) { res = FR_INVALID_NAME; return res; } if (*label == '/' || *label == '\\') { res = FR_INVALID_NAME; return res; } } return res; } FATFS *f_getfatfs(int vol) { FATFS *fs = NULL; if (vol < 0 || vol >= FF_VOLUMES) { fs = NULL; } else { fs = FatFs[vol]; } return fs; } static FRESULT FatfsCheckBoundParam(FATFS *fs, DWORD clust) { if (fs->st_clst <= 2 || (fs->st_clst + fs->ct_clst) > fs->n_fatent) { return FR_INT_ERR; } if (clust < 2 || clust > fs->n_fatent) { return FR_INT_ERR; } if (clust >= (fs->st_clst + fs->ct_clst) || clust < fs->st_clst) { return FR_CHAIN_ERR; } return FR_OK; } /* * check_boundary: * Check the chain occupied more than one virtual partition or not start at the var 'clust' * * Acceptable Return Value: * - FR_OK : The chain occupied only one virtual parition * - FR_CHAIN_ERR : The chain occupied more than one virtual parition */ FRESULT f_boundary(FATFS *fs, DWORD clust) { FFOBJID obj; FRESULT res; obj.fs = fs; if (fs == NULL) { return FR_INT_ERR; } if (fs->fs_type != FS_FAT32) { return FR_INVAILD_FATFS; } ENTER_FF(fs); res = FatfsCheckBoundParam(fs, clust); if (res != FR_OK) { LEAVE_FF(fs, res); } for (;;) { clust = get_fat(&obj, clust); if (clust == 0xFFFFFFFF) { LEAVE_FF(fs, FR_DISK_ERR); } if (clust == 0x0FFFFFFF) { break; } if (clust < 2 || clust >= fs->n_fatent) { LEAVE_FF(fs, FR_INT_ERR); } if (clust >= (fs->st_clst + fs->ct_clst) || clust < fs->st_clst) { LEAVE_FF(fs, FR_CHAIN_ERR); } } LEAVE_FF(fs, FR_OK); } /* * f_unregvirfs: * Uninitialized the CHILD FATFS object * * Acceptable Return Value: * - FR_OK : Successfully initialized the CHILD FATFS object. */ FRESULT f_disvirfs(FATFS *fs) { if (ISCHILD(fs)) { return FR_INVAILD_FATFS; } if (fs->vir_amount > _MAX_VIRVOLUMES) { return FR_INT_ERR; } ENTER_FF(fs); (void)f_unregvirfs(fs); LEAVE_FF(fs, FR_OK); } FRESULT f_unregvirfs(FATFS *fs) { UINT i; if (fs == NULL || ISCHILD(fs)) { return FR_INVAILD_FATFS; } fs->vir_avail = FS_VIRDISABLE; /* PARENT FATFS has linked to CHILD FATFS already */ if (fs->child_fs != NULL) { /* Followed the CHILD FATFS and free the memory */ for (i = 0; i < fs->vir_amount; i++) { if (CHILDFS(fs, i) != NULL) { ff_memfree(CHILDFS(fs, i)); } } /* Free the 'child_fs' feild */ ff_memfree(fs->child_fs); fs->child_fs = NULL; fs->vir_amount = 0xFFFFFFFF; } return FR_OK; } static void FatfsSetParentFs(FATFS *pfs, FATFS *fs) { pfs->fs_type = fs->fs_type; /* Copy the feild info from PARENT FATFS object */ pfs->pdrv = fs->pdrv; pfs->n_fats = fs->n_fats; pfs->id = fs->id; pfs->n_rootdir = fs->n_rootdir; pfs->csize = fs->csize; #if FF_MAX_SS != FF_MIN_SS pfs->ssize = fs->ssize; #endif pfs->sobj = fs->sobj; #if FF_FS_RPATH != 0 pfs->cdir = 0; #endif pfs->n_fatent = fs->n_fatent; pfs->fsize = fs->fsize; pfs->volbase = fs->volbase; pfs->fatbase = fs->fatbase; pfs->dirbase = fs->dirbase; pfs->database = fs->database; pfs->last_clst = 0xFFFFFFFF; /* Mark the 'last_clst' and 'free_clst' in CHILD FATFS is not been updated for now */ pfs->free_clst = 0xFFFFFFFF; pfs->st_clst = 0xFFFFFFFF; /* Mark the 'st_clst' and 'ct_clst' in CHILD FATFS is not been update for now. */ pfs->ct_clst = 0xFFFFFFFF; pfs->vir_flag = FS_CHILD; /* Mark the FATFS object is a CHILD */ pfs->vir_avail = FS_VIRENABLE; /* Mark the CHILD object is enable for now */ pfs->parent_fs = (void *)fs; /* Link to the PARENT object */ pfs->child_fs = (void *)NULL; /* Link the unrelated feild to NULL */ } /* * f_regvirfs: * Initialized the CHILD FATFS object * * Acceptable Return Value: * - FR_OK : Successfully initialized the CHILD FATFS object. * * Others Return Value: * - FR_INVAILD_FATFS : The FATFS object has error or the info in it has been occuried * - FR_DENIED : The virtual partition feature has been shut down by switcher * - FR_DISK_ERR : A disk error happened * - FR_NOT_ENOUGH_CORE : Not enough memory for allocate space for CHILD FATFS * - FR_INVALID_PARAMETER : There is a invaild value in current setting */ FRESULT f_regvirfs(FATFS *fs) { UINT i; FATFS *pfs = NULL; if (fs == NULL || ISCHILD(fs)) { return FR_INVAILD_FATFS; } if (fs->vir_amount > _MAX_VIRVOLUMES) { return FR_INT_ERR; } fs->parent_fs = (void *)fs; /* Relink to itself */ /* Mark the FATFS object is PARENT */ fs->st_clst = 0xFFFFFFFF; fs->ct_clst = 0xFFFFFFFF; /* Allocate a space for linking to the child FATFS */ fs->child_fs = (void **)ff_memalloc(fs->vir_amount * sizeof(void *)); if (fs->child_fs == NULL) { return FR_NOT_ENOUGH_CORE; } fs->vir_avail = FS_VIRENABLE; /* Mark the PARENT object is enable for now */ /* Set the CHILD object field */ for (i = 0; i < fs->vir_amount; i++) { pfs = ff_memalloc(sizeof(FATFS)); /* Allocate a memeory for current child FATFS object */ if (pfs == NULL) { /* If allocate failed, must call 'f_unregvirfs' to free the previous FATFS object memory */ goto ERROUT; } FatfsSetParentFs(pfs, fs); *(fs->child_fs + i) = (void *)pfs; } return FR_OK; ERROUT: while (i > 0) { --i; ff_memfree(*(fs->child_fs + i)); } ff_memfree(fs->child_fs); fs->child_fs = NULL; return FR_NOT_ENOUGH_CORE; } static FRESULT FatfsCheckScanFatParam(FATFS *fs) { if (fs == NULL) { return FR_INVAILD_FATFS; } if (ISNORMAL(fs)) { return FR_DENIED; } if (fs->fs_type != FS_FAT32 || ISPARENT(fs)) { return FR_INVAILD_FATFS; } if (fs->st_clst < 3 || fs->st_clst >= fs->n_fatent) { return FR_INVAILD_FATFS; } if (fs->ct_clst == 0 || fs->ct_clst > (fs->n_fatent - 3)) { return FR_INVAILD_FATFS; } if ((fs->st_clst + fs->ct_clst) > fs->n_fatent || (fs->st_clst + fs->ct_clst) < 3) { return FR_INVAILD_FATFS; } return FR_OK; } /* * f_scanfat: * Scan the FAT inside the boundary of CHILD FATFS limit, and update the free cluster and last cluster * * Acceptable Return Value: * - FR_OK : Successfully scaned the FAT and update field. * * Others Return Value: * - FR_INVAILD_FATFS : The FATFS object has error or the info in it has been occuried * - FR_DENIED : The virtual partition feature has been shut down by switcher * - FR_DISK_ERR : A disk error happened */ FRESULT f_scanfat(FATFS *fs) { FRESULT res; DWORD clst; DWORD link; FFOBJID obj; res = FatfsCheckScanFatParam(fs); if (res != FR_OK) { return res; } ENTER_FF(fs); res = FR_OK; obj.fs = fs; fs->free_clst = fs->ct_clst; for (clst = fs->st_clst; clst < fs->st_clst + fs->ct_clst; clst++) { link = get_fat(&obj, clst); if (link == 0xFFFFFFFF) { LEAVE_FF(fs, FR_DISK_ERR); } if (link == 0) { continue; } fs->free_clst--; } fs->last_clst = fs->st_clst - 1; LEAVE_FF(fs, res); } static FRESULT FatfsCheckStart(BYTE *work, FATFS *fs, BYTE vol) { DWORD startBaseSect, countBaseSect; countBaseSect = LD2PC(vol); /* Volume Base Sectors Count */ startBaseSect = LD2PS(vol); /* Volume Base Start Sector */ /* Check ASCII for Keyword "LITE" */ if (ld_dword(work + VR_VertifyString) != 0x4C495445) { return FR_NOVIRPART; } /* Check whether filesystem has been changed or not */ if (work[VR_PartitionFSType] != fs->fs_type) { return FR_MODIFIED; } /* Check whether volume base sector has benn changed or not */ if (ld_dword(work + VR_PartitionStSec) != startBaseSect) { return FR_MODIFIED; } /* Check whether volume base size hase been changed or not */ if (ld_dword(work + VR_PartitionCtSec) != countBaseSect) { return FR_MODIFIED; } /* Check whether volume cluster size has been changed or not */ if (ld_word(work + VR_PartitionClstSz) != fs->csize) { return FR_MODIFIED; } /* Check whether volume start cluster is cluster #3 or not */ if (ld_dword(work + VR_PartitionCtClst) != fs->n_fatent) { return FR_MODIFIED; } /* Check whether virtual partition overlimit */ if (work[VR_PartitionCnt] > _MAX_VIRVOLUMES) { return FR_MODIFIED; } return FR_OK; } static FRESULT FatfsCheckPercent(FATFS *fs, WORD i) { if ((CHILDFS(fs, i))->st_clst + (CHILDFS(fs, i))->ct_clst < fs->n_fatent) { fs->st_clst = (CHILDFS(fs, i))->st_clst + (CHILDFS(fs, i))->ct_clst; fs->ct_clst = fs->n_fatent - ((CHILDFS(fs, i))->st_clst + (CHILDFS(fs, i))->ct_clst); } else if ((CHILDFS(fs, i))->st_clst + (CHILDFS(fs, i))->ct_clst == fs->n_fatent) { fs->st_clst = 0xFFFFFFFF; fs->ct_clst = 0xFFFFFFFF; } else { (void)f_unregvirfs(fs); return FR_MODIFIED; } return FR_OK; } static FRESULT FatfsCheckPartClst(FATFS *fs, WORD i) { if (i == 0) { /* First virtual partition must start at cluster #3 */ if ((CHILDFS(fs, i))->st_clst != 3) { (void)f_unregvirfs(fs); return FR_MODIFIED; } } else { /* Check whether the current virtual partition is closely next to the previous virtual partition */ if ((CHILDFS(fs, i))->st_clst != (CHILDFS(fs, (i - 1))->st_clst + CHILDFS(fs, (i - 1))->ct_clst)) { (void)f_unregvirfs(fs); return FR_MODIFIED; } } return FR_OK; } static void FatfsSetChildClst(BYTE *work, FATFS *fs, WORD i) { (CHILDFS(fs, i))->st_clst = ld_dword(work + VR_PARTITION + i * VR_ITEMSIZE + VR_StartClust); (CHILDFS(fs, i))->ct_clst = ld_dword(work + VR_PARTITION + i * VR_ITEMSIZE + VR_CountClust); } /* * f_ckvtlpt : * Check the external SD virtual paritition sectors and read configure from it * * Acceptable Return Value: * - FR_OK : The external SD configure is complete, all info has been set to the * each CHILD FATFS * - FR_NOT_MATCHED : The virtual partition's configure does not matched as current setting * - FR_MODIFIED : The virtual partition's configure has been destoried partly or completely * - FR_NOVIRPART : The external SD has not been apllied as virtual partition yet * * Others Return Value: * - FR_INVAILD_FATFS : The FATFS object has error or the info in it has been occuried * - FR_DENIED : The virtual partition feature has been shut down by switcher * - FR_INVALID_DRIVE : The drive index is error * - FR_DISK_ERR : A Disk error happend */ FRESULT f_checkvirpart(FATFS *fs, const TCHAR *path, BYTE vol) { FRESULT res; WORD i; DWORD virSect; DWORD tmp; BYTE pdrv; BYTE *work = NULL; CHAR label[_MAX_ENTRYLENGTH + 1]; DWORD *labelTmp = NULL; /* to clear the compilation warning */ if (fs == NULL || (disk_status(fs->pdrv) & STA_NOINIT)) { return FR_INVAILD_FATFS; /* The object is invalid */ } /* Lock the filesystem object */ res = find_volume(&path, &fs, FA_WRITE); /* Update the filesystem info to the parent fs */ if (res != FR_OK) { LEAVE_FF(fs, res); } if (ISCHILD(fs)) { LEAVE_FF(fs, FR_INT_ERR); } /* Data will be save at the last reserve sector ,which is the front one of the fat base sector */ virSect = fs->fatbase - 1; pdrv = LD2PD(vol); /* Driver index */ work = (BYTE *)ff_memalloc(SS(fs)); if (work == NULL) { LEAVE_FF(fs, FR_NOT_ENOUGH_CORE); } /* Check and vertify partition information */ if (disk_read(pdrv, work, virSect, 1) != RES_OK) { res = FR_DISK_ERR; goto EXIT; } /* Load VBR */ res = FatfsCheckStart(work, fs, vol); if (res != FR_OK) { goto EXIT; } /* Check the virtual parition amount if matched current setting or not */ fs->vir_amount = work[VR_PartitionCnt]; res = f_regvirfs(fs); if (res != FR_OK) { goto EXIT; } for (i = 0; i < _MAX_VIRVOLUMES; i++) { if (i < work[VR_PartitionCnt]) { if (work[VR_PARTITION + i * VR_ITEMSIZE + VR_Available] != 0x80) { (void)f_unregvirfs(fs); res = FR_MODIFIED; goto EXIT; } } else { if (work[VR_PARTITION + i * VR_ITEMSIZE + VR_Available] != 0x00) { (void)f_unregvirfs(fs); res = FR_MODIFIED; goto EXIT; } break; } (void)memset_s(label, sizeof(label), 0, sizeof(label)); tmp = ld_dword(work + VR_PARTITION + i * VR_ITEMSIZE + VR_Entry + 0); labelTmp = (DWORD *)label; *labelTmp = tmp; tmp = ld_dword(work + VR_PARTITION + i * VR_ITEMSIZE + VR_Entry + 4); *((DWORD * )(label + 4)) = tmp; tmp = ld_dword(work + VR_PARTITION + i * VR_ITEMSIZE + VR_Entry + 8); *((DWORD * )(label + 8)) = tmp; tmp = ld_dword(work + VR_PARTITION + i * VR_ITEMSIZE + VR_Entry + 12); *((DWORD * )(label + 12)) = tmp; if (f_checkname(label) != FR_OK) { (void)f_unregvirfs(fs); res = FR_MODIFIED; goto EXIT; } (void)memcpy_s((CHILDFS(fs, i))->namelabel, _MAX_ENTRYLENGTH + 1, label, _MAX_ENTRYLENGTH + 1); FatfsSetChildClst(work, fs, i); /* External SD setting has overlimit the whole partition cluster amount */ if ((QWORD)(CHILDFS(fs, i))->st_clst + (QWORD)((CHILDFS(fs, i))->ct_clst) > (QWORD)fs->n_fatent) { (void)f_unregvirfs(fs); res = FR_MODIFIED; goto EXIT; } res = FatfsCheckPartClst(fs, i); if (res != FR_OK) { goto EXIT; } if (i == (work[VR_PartitionCnt] - 1)) { /* * If the external SD virtual partition percent exceeds the error tolerance based on current virtual * partition percent setting */ res = FatfsCheckPercent(fs, i); if (res != FR_OK) { goto EXIT; } } } EXIT: ff_memfree(work); LEAVE_FF(fs, res); } static void FatfsClacPartInfo(FATFS *fs, DOUBLE virpartper, UINT i) { if (i == 0) { (CHILDFS(fs, i))->st_clst = 3; (CHILDFS(fs, i))->ct_clst = (DWORD)((fs->n_fatent - 3) * g_fatVirPart.virtualinfo.virpartpercent[i]); fs->st_clst = (CHILDFS(fs, i))->st_clst + (CHILDFS(fs, i))->ct_clst; fs->ct_clst = fs->n_fatent - fs->st_clst; } else if (i != (fs->vir_amount - 1)) { (CHILDFS(fs, i))->st_clst = (CHILDFS(fs, (i - 1)))->st_clst + (CHILDFS(fs, (i - 1)))->ct_clst; (CHILDFS(fs, i))->ct_clst = (DWORD)((fs->n_fatent - 3) * g_fatVirPart.virtualinfo.virpartpercent[i]); } else { (CHILDFS(fs, i))->st_clst = (CHILDFS(fs, (i - 1)))->st_clst + (CHILDFS(fs, (i - 1)))->ct_clst; if (virpartper <= (1 + _FLOAT_ACC) && virpartper >= (1 - _FLOAT_ACC)) { (CHILDFS(fs, i))->ct_clst = fs->n_fatent - (CHILDFS(fs, i))->st_clst; fs->st_clst = 0xFFFFFFFF; fs->ct_clst = 0xFFFFFFFF; } else { (CHILDFS(fs, i))->ct_clst = (DWORD)((fs->n_fatent - 3) * g_fatVirPart.virtualinfo.virpartpercent[i]); fs->st_clst = (CHILDFS(fs, i))->st_clst + (CHILDFS(fs, i))->ct_clst; fs->ct_clst = fs->n_fatent - fs->st_clst; } } } /* * f_mkvtlpt: * Apply the current virtual partition's setting to external SD card and to the CHILD FATFS * * Acceptable Return Value: * - FR_OK : Successfully applied current setting to external SD card and * CHILD FATFS * * Others Return Value : * - FR_INVAILD_FATFS : The FATFS object has error or the info in it has been occuried * - FR_DENIED : The virtual partition feature has been shut down by switcher * - FR_INVALID_DRIVE : The drive index is error * - FR_DISK_ERR : A Disk error happend */ FRESULT f_makevirpart(FATFS *fs, const TCHAR *path, BYTE vol) { FRESULT res; DWORD virSect; DWORD startBaseSect, countBaseSect; DWORD tmp; CHAR label[_MAX_ENTRYLENGTH + 1]; DWORD *labelTmp = NULL; /* to clear the compilation warning */ UINT i; BYTE pdrv; BYTE *work = NULL; DOUBLE virpartper = 0.0; if (fs == NULL || (disk_status(fs->pdrv) & STA_NOINIT)) { return FR_INVAILD_FATFS; /* The object is invalid */ } /* Lock the filesystem object */ res = find_volume(&path, &fs, FA_WRITE); /* Update the filesystem info to the parent fs */ if (res != FR_OK) { LEAVE_FF(fs, res); } /* Only available in FAT32 filesystem */ if (ISCHILD(fs)) { LEAVE_FF(fs, FR_INVAILD_FATFS); } /* Data will be save at the last reserve sector,which is the front one of the fat base sector */ virSect = fs->fatbase - 1; /* Search the fs index, which same as the volume index */ pdrv = LD2PD(vol); /* Driver index */ countBaseSect = LD2PC(vol); /* Volume Base Sectors Count */ startBaseSect = LD2PS(vol); /* Volume Base Start Sector */ fs->vir_amount = g_fatVirPart.virtualinfo.virpartnum; res = f_regvirfs(fs); if (res != FR_OK) { LEAVE_FF(fs, res); } work = (BYTE *)ff_memalloc(SS(fs)); if (work == NULL) { LEAVE_FF(fs, FR_NOT_ENOUGH_CORE); } /* Data Cluster is begin from the Cluster #3 to the last cluster */ /* Cluster #0 #1 is for VBR, reserve sectors and fat */ /* Cluster #2 is for root directory */ (void)memset_s(work, SS(fs), 0, SS(fs)); for (i = 0; i < fs->vir_amount; i++) { /* Copy the Entry label and write to work sector's buffer */ (void)memset_s(label, sizeof(label), 0, sizeof(label)); (void)memcpy_s(label, _MAX_ENTRYLENGTH + 1, g_fatVirPart.virtualinfo.virpartname[i], _MAX_ENTRYLENGTH + 1); labelTmp = (DWORD *)label; tmp = *labelTmp; st_dword(work + VR_PARTITION + i * VR_ITEMSIZE + VR_Entry + 0, tmp); tmp = *((DWORD * )(label + 4)); st_dword(work + VR_PARTITION + i * VR_ITEMSIZE + VR_Entry + 4, tmp); tmp = *((DWORD * )(label + 8)); st_dword(work + VR_PARTITION + i * VR_ITEMSIZE + VR_Entry + 8, tmp); tmp = *((DWORD * )(label + 12)); st_dword(work + VR_PARTITION + i * VR_ITEMSIZE + VR_Entry + 12, tmp); virpartper += g_fatVirPart.virtualinfo.virpartpercent[i]; (void)memcpy_s((CHILDFS(fs, i))->namelabel, _MAX_ENTRYLENGTH + 1, g_fatVirPart.virtualinfo.virpartname[i], _MAX_ENTRYLENGTH + 1); FatfsClacPartInfo(fs, virpartper, i); (CHILDFS(fs, i))->last_clst = (CHILDFS(fs, i))->st_clst - 1; work[VR_PARTITION + i * VR_ITEMSIZE + VR_Available] = 0x80; } /* Set the data to sector */ work[VR_PartitionCnt] = fs->vir_amount; work[VR_PartitionFSType] = fs->fs_type; st_dword(work + VR_PartitionStSec, startBaseSect); st_dword(work + VR_PartitionCtSec, countBaseSect); st_word(work + VR_PartitionClstSz, fs->csize); st_dword(work + VR_PartitionCtClst, fs->n_fatent); for (i = 0; i < fs->vir_amount; i++) { st_dword(work + VR_PARTITION + i * VR_ITEMSIZE + VR_StartClust, (CHILDFS(fs, i))->st_clst); st_dword(work + VR_PARTITION + i * VR_ITEMSIZE + VR_CountClust, (CHILDFS(fs, i))->ct_clst); } /* ASCII for Keyword "LITE" */ st_dword(work + VR_VertifyString, 0x4C495445); /* Write into the data area */ if (disk_write(pdrv, work, virSect, 1) != RES_OK) { (void)f_unregvirfs(fs); res = FR_DISK_ERR; } ff_memfree(work); LEAVE_FF(fs, res); } FRESULT f_getvirfree(const TCHAR *path, DWORD *nclst, DWORD *cclst) { FATFS *fs = NULL; FRESULT res; DWORD clst, link; DWORD nfree; UINT i; DIR dj; /* Find volume to Update the global FSINFO */ res = find_volume(&path, &fs, 0); if (res != FR_OK) { LEAVE_FF(fs, res); } /* Following the entry keyword, decide to replace the PARENT FATFS to CHILD FATFS or not */ dj.obj.fs = fs; if (ISVIRPART(fs)) { /* Check the virtual partition top directory, and match the virtual fs */ res = follow_virentry(&dj.obj, path); if (res == FR_INT_ERR) { LEAVE_FF(fs, res); } if (res == FR_OK) { fs = dj.obj.fs; } } else { /* Virtual Partition Feature was off, deny this operation */ LEAVE_FF(fs, FR_DENIED); } /* If current FATFS is a CHILD FATFS */ if (ISCHILD(fs)) { /* If CHILD FATFS' free_clst is invaild, the scan the FAT and update it */ if (fs->free_clst > fs->ct_clst) { dj.obj.fs = fs; fs->free_clst = fs->ct_clst; for (clst = fs->st_clst; clst < fs->st_clst + fs->ct_clst; clst++) { link = get_fat(&dj.obj, clst); if (link == 0) { continue; } fs->free_clst--; } } *nclst = fs->free_clst; *cclst = fs->ct_clst; LEAVE_FF(fs, FR_OK); } else { nfree = 0; if (fs->ct_clst == 0xFFFFFFFF) { LEAVE_FF(fs, FR_DENIED); } for (i = 0; i < fs->vir_amount; i++) { if (CHILDFS(fs, i)->free_clst > CHILDFS(fs, i)->ct_clst) { dj.obj.fs = CHILDFS(fs, i); CHILDFS(fs, i)->free_clst = CHILDFS(fs, i)->ct_clst; for (clst = CHILDFS(fs, i)->st_clst; clst < CHILDFS(fs, i)->st_clst + CHILDFS(fs, i)->ct_clst; clst++) { link = get_fat(&dj.obj, clst); if (link == 0) { continue; } CHILDFS(fs, i)->free_clst--; } } nfree += CHILDFS(fs, i)->free_clst; } *nclst = fs->free_clst - nfree; *cclst = fs->ct_clst; LEAVE_FF(fs, FR_OK); } } #endif